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,72 @@
1
+ # Use this to fill in an entire form with data from a table. Example:
2
+ #
3
+ # When I fill in the following:
4
+ # | | Account Number | 5002 |
5
+ # | | Expiry date | 2009-11-01 |
6
+ # | | Note | Nice guy |
7
+ # | | Wants Email? | |
8
+ # | User data | Sex (select) | Male |
9
+ # | | Avatar | avatar.png |
10
+ # | | Due date | 12:35 |
11
+ # | Additional data | Accept user agrement | true |
12
+ # | | Send me letters | false |
13
+ # | | radio 1 | true |
14
+ #
15
+ When(/^(?:|I )fill in the following:( within (?:.*))?$/,
16
+ &Pickles::StepDef.define_table_step([2, 3]) do |label, value, within|
17
+
18
+ FillIN::Factory.new(label, value, within: current_within).call.call
19
+
20
+ end)
21
+
22
+ # Waiter.wait_for_ajax
23
+ #
24
+ # if fields.headers.length == 3
25
+ # current_within = within
26
+ #
27
+ # rows = fields.rows.unshift(fields.headers)
28
+ #
29
+ # rows.each do |(within, label, value)|
30
+ # case within
31
+ # when Helpers::Regex::WITHIN
32
+ # current_within = Pickles.detect_node($1, $2, within: within)
33
+ # when "-"
34
+ # current_within = within
35
+ # end
36
+ #
37
+ # if label['pry']
38
+ # label['pry'] = ''
39
+ #
40
+ # pry binding
41
+ # end
42
+ #
43
+ # FillIN::Factory.new(label, value, within: current_within).call.call
44
+ # end
45
+ # elsif fields.headers.length == 2
46
+ # fields.rows_hash.each do |label, value|
47
+ # pry binding if label['pry']
48
+ # FillIN::Factory.new(label, value, within: within).call.call
49
+ # end
50
+ # else
51
+ # raise(ArgumentError, 'Unsupported table type. Must contain 2 or 3 columns')
52
+ # end
53
+ #
54
+ # end
55
+
56
+ # When /^(?:|I ) select "([^"]*)" from "([^"]*)"( within (?:.*))?$/ do |value, label, within|
57
+ # FillIN::Select.new(label, value, within).call
58
+ # end
59
+
60
+ When /^(?:|I )(fill|select|unselect)(?: "([^"]*)")?(?: with "([^"]*)")?( within (?:.*))?$/ do |type, labels, value, within|
61
+ if type == 'select' && value.present?
62
+ FillIN::Select.new(labels, value, within).call
63
+ else
64
+ labels.split(/\s*\|\s*/).each do |label|
65
+ FillIN::Factory.new(label, value, within: within).call.call
66
+ end
67
+ end
68
+ end
69
+
70
+ When /^(?:|I )attach the file "([^"]*)" to "([^"]*)"( within (?:.*))?$/ do |file_name, label, within|
71
+ FillIN::Input.new(label, file_name, within).call
72
+ end
@@ -0,0 +1,19 @@
1
+ class FillIN::ComplexInput
2
+
3
+ include Pickles
4
+
5
+ def initialize(label, value, within)
6
+ @label = label
7
+ @value = value
8
+ @within = within || Capybara.current_session
9
+ end
10
+
11
+ def call
12
+ @value.split(/\s*:\s*/).each.with_index do |value, index|
13
+ input_locator = "#{label}[#{index}]"
14
+
15
+ FillIN::Input.new(input_locator, value, @within)
16
+ end
17
+ end
18
+
19
+ end
@@ -0,0 +1,25 @@
1
+ class FillIN::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 = FillIN::ComplexInput
14
+ elsif @label =~ TAG
15
+ @label = $1
16
+ tag = $2
17
+ step = Pickles.config.step_by_tag(tag) || FillIN::Input
18
+ else
19
+ step = FillIN::Input
20
+ end
21
+
22
+ step.new(@label, @value, @within)
23
+ end
24
+
25
+ end
@@ -0,0 +1,29 @@
1
+ class FillIN::Input
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
+ case input.native.attribute("type")
11
+
12
+ when "radio", "checkbox"
13
+ Pickles.select_input(input, @value)
14
+ when "file"
15
+ Pickles.attach_file(input, @value)
16
+ else # password email tel ...
17
+ input.set(@value)
18
+ end
19
+
20
+ input
21
+ end
22
+
23
+ private
24
+
25
+ def input
26
+ @input ||= Pickles.find_input(@label, within: @within)
27
+ end
28
+
29
+ end
@@ -0,0 +1,30 @@
1
+ class FillIN::Select
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
+ input = FillIN::Input.new(@label, @value, @within).call
11
+
12
+ text, selector = NodeTextLookup.lookup_values(value)
13
+ item_xpath = selector.(text)
14
+
15
+ Waiter.wait do
16
+ input.find(:xpath, "./ancestor::*[#{item_xpath}][1]/#{item_xpath}").click
17
+ end
18
+
19
+ Pickles.blur(input)
20
+
21
+ Waiter.wait_for_ajax
22
+
23
+ input
24
+ end
25
+
26
+ private
27
+
28
+ attr_reader :label, :value
29
+
30
+ end
@@ -0,0 +1,3 @@
1
+ When 'I go back' do
2
+ page.evaluate_script('history.back()')
3
+ end
@@ -0,0 +1,13 @@
1
+ within_reg = /\A\s*(.*)?\s*(?:["|'](.*?)["|'])?\s*\Z/
2
+
3
+ Transform(/(within .*)$/) do |within_info|
4
+ splitted = within_info.split('within').reject(&:blank?)
5
+
6
+ splitted.reverse_each.each_with_object(page) do |info, within|
7
+ captures = Helpers::Regex::WITHIN.match(info).captures
8
+ el_alias = captures[0]
9
+ locator = captures[1]
10
+
11
+ Pickles.detect_node(el_alias, locator, within: within)
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Pickles
2
+ VERSION = "0.1.0"
3
+ end
data/lib/pickles.rb ADDED
@@ -0,0 +1,3 @@
1
+ # module Pickles
2
+ # Your code goes here...
3
+ # end
data/pickles.gemspec ADDED
@@ -0,0 +1,36 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'cucumber/pickles/version'
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = "pickles"
8
+ s.version = Pickles::VERSION
9
+ s.authors = ["vs"]
10
+ s.email = ["vshaveyko@gmail.com"]
11
+
12
+ s.summary = %q{Capybara test helpers}
13
+ s.description = %q{Set of common everyday steps with shortcuts}
14
+ s.homepage = "https://github.com/vshaveyko/pickles"
15
+ s.license = "MIT"
16
+
17
+ # Prevent pushing this gem to RubyGems.org. To allow pushes either set the 'allowed_push_host'
18
+ # to allow pushing to a single host or delete this section to allow pushing to any host.
19
+ # if s.respond_to?(:metadata)
20
+ # s.metadata['allowed_push_host'] = "TODO: Set to 'http://mygemserver.com'"
21
+ # else
22
+ # raise "RubyGems 2.0 or newer is required to protect against " \
23
+ # "public gem pushes."
24
+ # end
25
+
26
+ s.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|s|features)/}) }
27
+ s.executables = s.files.grep(%r{^exe/}) { |f| File.basename(f) }
28
+
29
+ s.add_dependency 'capybara', '>= 1.1.2'
30
+
31
+ s.require_paths = ["lib"]
32
+
33
+ s.add_development_dependency "bundler", "~> 1.13"
34
+ s.add_development_dependency "rake", "~> 10.0"
35
+ s.add_development_dependency "rs", "~> 3.0"
36
+ end
@@ -0,0 +1,155 @@
1
+ require 'spec_helper'
2
+
3
+ RSpec.describe '#NodeFinders', :Helpers, :NodeFinders do
4
+
5
+ # let(:finder) { Object.new.extend(NodeFinders) }
6
+ # let(:stub) { Object.new }
7
+
8
+ describe '#find_node', :find_node do
9
+
10
+ before(:each) { @session.visit('/with_html') }
11
+
12
+ it 'raises ambgious error' do
13
+ expect { Pickles.find_node('A link') }.to raise_error(Pickles::Ambiguous)
14
+ end
15
+
16
+ it 'Finds element with exact text with = ' do
17
+ expect(Pickles.find_node('=A link')).to have_attributes path: "/html/body/p[3]/a[3]"
18
+ end
19
+
20
+ it 'Finds element by index with [1]' do
21
+ expect(Pickles.find_node('A link[1]')).to have_attributes path: "/html/body/p[3]/a[2]"
22
+ end
23
+
24
+ end
25
+
26
+ describe '#find_input', :find_input do
27
+
28
+ class TestApp
29
+
30
+ get '/with/inputs' do
31
+ <<~HTML
32
+ <!-- Case1 -->
33
+ <div> <div> <span>LABEL_INPUT</span> </div>
34
+ <input type="text" value="LABEL_INPUT_VALUE" />
35
+ </div>
36
+
37
+ <!-- Case2 -->
38
+ <input type="text" placeholder="PLACEHOLDER_INPUT" value="PLACEHOLDER_INPUT_VALUE" />
39
+
40
+ <!-- Case3 -->
41
+ <div> <div> <span>TEXTAREA_LABEL</span> </div>
42
+ <textarea>TEXTAREA_VALUE</textarea>
43
+ </div>
44
+
45
+ <!-- Case4 -->
46
+ <div contenteditable="true" placeholder="CONTENTEDITABLE_PLACEHOLDER">
47
+ CONTENTEDITABLE_PLACEHOLDER_VALUE
48
+ </div>
49
+
50
+ <!-- Case5 -->
51
+ <div> <div> <span>CONTENTEDITABLE_LABEL</span> </div>
52
+ <div contenteditable="true">
53
+ CONTENTEDITABLE_LABEL_VALUE
54
+ </div>
55
+ </div>
56
+ HTML
57
+ end
58
+
59
+ end
60
+
61
+ before(:each) { @session.visit('/with/inputs') }
62
+
63
+ describe 'input' do
64
+ it 'by span label' do
65
+ expect(
66
+ Pickles.find_input('LABEL_INPUT')
67
+ ).to have_attributes value: "LABEL_INPUT_VALUE"
68
+ end
69
+
70
+ it 'by placeholder' do
71
+ expect(
72
+ Pickles.find_input('PLACEHOLDER_INPUT')
73
+ ).to have_attributes value: "PLACEHOLDER_INPUT_VALUE"
74
+ end
75
+ end
76
+
77
+ describe 'textarea' do
78
+ it 'by span label' do
79
+ expect(
80
+ Pickles.find_input('TEXTAREA_LABEL')
81
+ ).to have_attributes value: "TEXTAREA_VALUE"
82
+ end
83
+ end
84
+
85
+ describe 'contenteditable' do
86
+ it 'by placeholder' do
87
+ expect(
88
+ Pickles.find_input('CONTENTEDITABLE_PLACEHOLDER')
89
+ ).to have_attributes text: "CONTENTEDITABLE_PLACEHOLDER_VALUE"
90
+ end
91
+
92
+ it 'by span label' do
93
+ expect(
94
+ Pickles.find_input('CONTENTEDITABLE_LABEL')
95
+ ).to have_attributes text: "CONTENTEDITABLE_LABEL_VALUE"
96
+ end
97
+ end
98
+
99
+ end
100
+
101
+ describe '#detect_node', :detect_node do
102
+
103
+ class TestApp
104
+
105
+ get '/with/divs' do
106
+ <<~HTML
107
+ <!-- Case1 -->
108
+ <div class="TEST_CLASS">TEST_CLASS_VALUE</div>
109
+
110
+ <!-- Case2 -->
111
+ <div> <div id="TEST_ID">TEST_ID_VALUE</div> </div>
112
+
113
+ <!-- Case3 -->
114
+ <custom-tag>CUSTOM_TAG_VALUE[1]</custom-tag>
115
+ <custom-tag>CUSTOM_TAG_VALUE[2]</custom-tag>
116
+ HTML
117
+ end
118
+
119
+ end
120
+
121
+ before(:each) { @session.visit('/with/divs') }
122
+
123
+ it 'finds by xpath_node_map' do
124
+ Pickles.configure do |c|
125
+ c.xpath_node_map = {
126
+ xpath_test_node: "//div[@id='TEST_ID']"
127
+ }
128
+ end
129
+
130
+ expect(
131
+ Pickles.detect_node(:xpath_test_node)
132
+ ).to have_attributes text: 'TEST_ID_VALUE'
133
+ end
134
+
135
+ it 'finds by css_node_map' do
136
+ Pickles.configure do |c|
137
+ c.css_node_map = {
138
+ css_test_node: ".TEST_CLASS"
139
+ }
140
+ end
141
+
142
+ expect(
143
+ Pickles.detect_node(:css_test_node)
144
+ ).to have_attributes text: 'TEST_CLASS_VALUE'
145
+ end
146
+
147
+ it 'fallbacks to given value as css tag' do
148
+ expect(
149
+ Pickles.detect_node('custom-tag[1]')
150
+ ).to have_attributes text: 'CUSTOM_TAG_VALUE[1]'
151
+ end
152
+
153
+ end
154
+
155
+ end
@@ -0,0 +1,41 @@
1
+ require 'spec_helper'
2
+
3
+ require 'cucumber/pickles/helpers/waiter'
4
+
5
+ RSpec.describe '#Waiter' do
6
+
7
+ it 'Return 0 pending requests on page load' do
8
+ @session.visit('/with_js')
9
+
10
+ expect(Waiter.pending_ajax_requests_num).to eq 0
11
+ end
12
+
13
+ it 'Return 1 pending requests with open request' do
14
+ @session.visit('/with_js')
15
+
16
+ @session.evaluate_script(
17
+ <<~JS
18
+ new XMLHttpRequest().open("GET", '')
19
+ JS
20
+ )
21
+
22
+ expect(Waiter.pending_ajax_requests_num).to eq 1
23
+ end
24
+
25
+ it 'Return 0 pending requests with closed request' do
26
+ @session.visit('/with_js')
27
+
28
+ @session.evaluate_script("window.req = new XMLHttpRequest();")
29
+
30
+ @session.evaluate_script("window.req.open('GET', '', true);")
31
+
32
+ expect(Waiter.pending_ajax_requests_num).to eq 1
33
+
34
+ @session.evaluate_script("window.req.send();")
35
+
36
+ @session.evaluate_script("window.req.abort();")
37
+
38
+ expect(Waiter.pending_ajax_requests_num).to eq 0
39
+ end
40
+
41
+ end