pickles 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +5 -0
  5. data/Gemfile +10 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +196 -0
  8. data/Rakefile +6 -0
  9. data/bin/console +14 -0
  10. data/bin/setup +8 -0
  11. data/lib/cucumber/pickles.rb +6 -0
  12. data/lib/cucumber/pickles/check_in.rb +10 -0
  13. data/lib/cucumber/pickles/config.rb +61 -0
  14. data/lib/cucumber/pickles/errors/ambigious.rb +27 -0
  15. data/lib/cucumber/pickles/errors/node_find_error.rb +37 -0
  16. data/lib/cucumber/pickles/fill_in.rb +10 -0
  17. data/lib/cucumber/pickles/helpers.rb +40 -0
  18. data/lib/cucumber/pickles/helpers/extensions/chrome/.DS_Store +0 -0
  19. data/lib/cucumber/pickles/helpers/extensions/chrome/compiled.crx.base64 +1 -0
  20. data/lib/cucumber/pickles/helpers/extensions/chrome/manifest.json +15 -0
  21. data/lib/cucumber/pickles/helpers/extensions/chrome/src/.DS_Store +0 -0
  22. data/lib/cucumber/pickles/helpers/extensions/chrome/src/inject/inject.js +35 -0
  23. data/lib/cucumber/pickles/helpers/main.rb +88 -0
  24. data/lib/cucumber/pickles/helpers/node_finders.rb +125 -0
  25. data/lib/cucumber/pickles/helpers/regex.rb +6 -0
  26. data/lib/cucumber/pickles/helpers/waiter.rb +152 -0
  27. data/lib/cucumber/pickles/locator/equal.rb +26 -0
  28. data/lib/cucumber/pickles/locator/index.rb +20 -0
  29. data/lib/cucumber/pickles/refinements.rb +49 -0
  30. data/lib/cucumber/pickles/steps.rb +73 -0
  31. data/lib/cucumber/pickles/steps/can_see.rb +70 -0
  32. data/lib/cucumber/pickles/steps/check.rb +55 -0
  33. data/lib/cucumber/pickles/steps/check_in/complex_input.rb +17 -0
  34. data/lib/cucumber/pickles/steps/check_in/factory.rb +26 -0
  35. data/lib/cucumber/pickles/steps/check_in/input.rb +36 -0
  36. data/lib/cucumber/pickles/steps/check_in/text.rb +17 -0
  37. data/lib/cucumber/pickles/steps/click.rb +91 -0
  38. data/lib/cucumber/pickles/steps/fill.rb +72 -0
  39. data/lib/cucumber/pickles/steps/fill_in/complex_input.rb +19 -0
  40. data/lib/cucumber/pickles/steps/fill_in/factory.rb +25 -0
  41. data/lib/cucumber/pickles/steps/fill_in/input.rb +29 -0
  42. data/lib/cucumber/pickles/steps/fill_in/select.rb +30 -0
  43. data/lib/cucumber/pickles/steps/redirect.rb +3 -0
  44. data/lib/cucumber/pickles/transform.rb +13 -0
  45. data/lib/cucumber/pickles/version.rb +3 -0
  46. data/lib/pickles.rb +3 -0
  47. data/pickles.gemspec +36 -0
  48. data/spec/helpers/node_finders_spec.rb +155 -0
  49. data/spec/helpers/waiter_spec.rb +41 -0
  50. data/spec/locator_spec.rb +31 -0
  51. data/spec/spec_helper.rb +32 -0
  52. data/spec/step_def_spec.rb +0 -0
  53. data/spec/steps/check_in/factory_spec.rb +52 -0
  54. data/spec/steps/fill_in/factory_spec.rb +51 -0
  55. 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