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