page_record 0.4.0 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/.rubocop.yml +13 -0
  2. data/CHANGES.md +5 -0
  3. data/Gemfile.lock +46 -14
  4. data/Guardfile +24 -0
  5. data/README.md +27 -1
  6. data/bin/autospec +16 -0
  7. data/bin/guard +16 -0
  8. data/bin/rake +16 -0
  9. data/bin/rspec +16 -0
  10. data/lib/page_record/attribute_accessors.rb +57 -54
  11. data/lib/page_record/base.rb +27 -22
  12. data/lib/page_record/class_actions.rb +47 -50
  13. data/lib/page_record/class_methods.rb +252 -261
  14. data/lib/page_record/errors.rb +81 -82
  15. data/lib/page_record/finders.rb +129 -131
  16. data/lib/page_record/form_builder.rb +4 -4
  17. data/lib/page_record/formtastic.rb +20 -9
  18. data/lib/page_record/helpers.rb +192 -131
  19. data/lib/page_record/inspector.rb +36 -0
  20. data/lib/page_record/instance_actions.rb +44 -46
  21. data/lib/page_record/rspec.rb +1 -1
  22. data/lib/page_record/validation.rb +46 -0
  23. data/lib/page_record/version.rb +1 -1
  24. data/lib/page_record.rb +13 -15
  25. data/page_record.gemspec +4 -1
  26. data/spec/.rubocop.yml +4 -0
  27. data/spec/helpers_spec.rb +109 -100
  28. data/spec/inspector_spec.rb +70 -0
  29. data/spec/page_record_spec.rb +357 -388
  30. data/spec/spec_helper.rb +1 -3
  31. data/spec/support/shared_contexts.rb +24 -2
  32. data/spec/support/shared_examples.rb +41 -45
  33. data/spec/support/team.rb +4 -4
  34. data/spec/support/test_app.rb +10 -13
  35. data/spec/support/views/page-with-1-error.erb +5 -0
  36. data/spec/support/views/page-with-2-errors-on-different-attributes.erb +9 -0
  37. data/spec/support/views/page-with-2-errors-on-same-attribute.erb +6 -0
  38. data/spec/support/views/page-without-errors.erb +4 -0
  39. data/spec/validation_spec.rb +142 -0
  40. data/tmp/rspec_guard_result +1 -0
  41. metadata +80 -5
data/.rubocop.yml ADDED
@@ -0,0 +1,13 @@
1
+ Encoding:
2
+ Enabled: false
3
+
4
+ # Limit lines to 80 characters.
5
+ LineLength:
6
+ Enabled: false
7
+
8
+ Documentation:
9
+ Enabled: false
10
+
11
+ MethodLength:
12
+ CountComments: false # count full line comments?
13
+ Max: 15
data/CHANGES.md CHANGED
@@ -15,3 +15,8 @@
15
15
  ##V0.4.0
16
16
  * Added support for Formtasic
17
17
  * Added spec's for rails helpers
18
+
19
+ ##V0.5.0
20
+ * Added a rails project containing some cucumber features to demonstrate how it works. The example also shows how to use the rails and Formtastic helpers.
21
+ * Added support for form validations. Both in formtastic and in reading the errors
22
+ * added a `inspect` for both the class and for the instances. This is __very__ helpfull when dubugging.
data/Gemfile.lock CHANGED
@@ -1,7 +1,8 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- page_record (0.4.0)
4
+ page_record (0.5.0)
5
+ activemodel
5
6
  activesupport
6
7
  capybara (~> 2.1.0)
7
8
 
@@ -38,6 +39,7 @@ GEM
38
39
  rack (>= 1.0.0)
39
40
  rack-test (>= 0.5.4)
40
41
  xpath (~> 2.0)
42
+ coderay (1.0.9)
41
43
  colorize (0.5.8)
42
44
  coveralls (0.6.7)
43
45
  colorize
@@ -47,13 +49,34 @@ GEM
47
49
  thor
48
50
  diff-lcs (1.2.4)
49
51
  erubis (2.7.0)
52
+ ffi (1.9.0)
53
+ formatador (0.2.4)
54
+ guard (1.8.1)
55
+ formatador (>= 0.2.4)
56
+ listen (>= 1.0.0)
57
+ lumberjack (>= 1.0.2)
58
+ pry (>= 0.9.10)
59
+ thor (>= 0.14.6)
60
+ guard-rspec (3.0.2)
61
+ guard (>= 1.8)
62
+ rspec (~> 2.13)
50
63
  i18n (0.6.4)
64
+ listen (1.2.2)
65
+ rb-fsevent (>= 0.9.3)
66
+ rb-inotify (>= 0.9)
67
+ rb-kqueue (>= 0.2)
68
+ lumberjack (1.0.4)
69
+ method_source (0.8.1)
51
70
  mime-types (1.23)
52
- mini_portile (0.5.0)
71
+ mini_portile (0.5.1)
53
72
  minitest (4.7.5)
54
73
  multi_json (1.7.7)
55
74
  nokogiri (1.6.0)
56
75
  mini_portile (~> 0.5.0)
76
+ pry (0.9.12.2)
77
+ coderay (~> 1.0.5)
78
+ method_source (~> 0.8)
79
+ slop (~> 3.4)
57
80
  rack (1.5.2)
58
81
  rack-protection (1.5.0)
59
82
  rack
@@ -65,23 +88,29 @@ GEM
65
88
  rake (>= 0.8.7)
66
89
  thor (>= 0.18.1, < 2.0)
67
90
  rake (10.1.0)
91
+ rb-fsevent (0.9.3)
92
+ rb-inotify (0.9.0)
93
+ ffi (>= 0.5.0)
94
+ rb-kqueue (0.2.0)
95
+ ffi (>= 0.5.0)
68
96
  rest-client (1.6.7)
69
97
  mime-types (>= 1.16)
70
- rspec (2.13.0)
71
- rspec-core (~> 2.13.0)
72
- rspec-expectations (~> 2.13.0)
73
- rspec-mocks (~> 2.13.0)
74
- rspec-core (2.13.1)
75
- rspec-expectations (2.13.0)
98
+ rspec (2.14.1)
99
+ rspec-core (~> 2.14.0)
100
+ rspec-expectations (~> 2.14.0)
101
+ rspec-mocks (~> 2.14.0)
102
+ rspec-core (2.14.4)
103
+ rspec-expectations (2.14.0)
76
104
  diff-lcs (>= 1.1.3, < 2.0)
77
- rspec-mocks (2.13.1)
78
- rspec-rails (2.13.2)
105
+ rspec-mocks (2.14.1)
106
+ rspec-rails (2.14.0)
79
107
  actionpack (>= 3.0)
80
108
  activesupport (>= 3.0)
81
109
  railties (>= 3.0)
82
- rspec-core (~> 2.13.0)
83
- rspec-expectations (~> 2.13.0)
84
- rspec-mocks (~> 2.13.0)
110
+ rspec-core (~> 2.14.0)
111
+ rspec-expectations (~> 2.14.0)
112
+ rspec-mocks (~> 2.14.0)
113
+ ruby_gntp (0.3.4)
85
114
  simplecov (0.7.1)
86
115
  multi_json (~> 1.0)
87
116
  simplecov-html (~> 0.7.1)
@@ -90,8 +119,9 @@ GEM
90
119
  rack (~> 1.4)
91
120
  rack-protection (~> 1.4)
92
121
  tilt (~> 1.3, >= 1.3.4)
122
+ slop (3.4.6)
93
123
  thor (0.18.1)
94
- thread_safe (0.1.0)
124
+ thread_safe (0.1.2)
95
125
  atomic
96
126
  tilt (1.4.1)
97
127
  tzinfo (0.3.37)
@@ -106,8 +136,10 @@ DEPENDENCIES
106
136
  activerecord
107
137
  bundler (~> 1.3)
108
138
  coveralls
139
+ guard-rspec
109
140
  page_record!
110
141
  rake
111
142
  rspec
112
143
  rspec-rails
144
+ ruby_gntp
113
145
  sinatra
data/Guardfile ADDED
@@ -0,0 +1,24 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec' do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
11
+ watch(%r{^app/(.*)(\.erb|\.haml)$}) { |m| "spec/#{m[1]}#{m[2]}_spec.rb" }
12
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
13
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
14
+ watch('config/routes.rb') { "spec/routing" }
15
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
16
+
17
+ # Capybara features specs
18
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/features/#{m[1]}_spec.rb" }
19
+
20
+ # Turnip features and steps
21
+ watch(%r{^spec/acceptance/(.+)\.feature$})
22
+ watch(%r{^spec/acceptance/steps/(.+)_steps\.rb$}) { |m| Dir[File.join("**/#{m[1]}.feature")][0] || 'spec/acceptance' }
23
+ end
24
+
data/README.md CHANGED
@@ -7,7 +7,33 @@ You've probably been there. You're building your killer Web Application. You, be
7
7
  ## PageRecord can help.
8
8
  There are a lot of ways you can do this. PageRecord is one of these ways. PageRecord is an ActiveRecord like abstraction for information on the HTML page. You can use `TeamRecord.find(1)` and `TeamRecord.find_by_name('Barcelona')` like functions to find a record on an HTML page. When you've found the record you need, you can use easy accessors to access the attributes. `TeamRecord.find(1).name` returns the name of the team.
9
9
 
10
- TODO explain how it helps in testing
10
+ ###Adding a record using a form
11
+
12
+ With the PageRecord api you can fill all form fields press the save button.
13
+
14
+ ```ruby
15
+ When(/^I add a team$/) do
16
+ visit new_team_path
17
+ team = TeamPage.find
18
+ team.name = "Ajax"
19
+ team.competition = @competition
20
+ team.save
21
+ end
22
+ ```
23
+
24
+ ###Checking if an error is displayed.
25
+
26
+ With the PageRecord api you can check for errors. No need to checkout what css you need to select the right DOM element. PageRecord takes care of that.
27
+
28
+ ```ruby
29
+ Then(/^I should see error "can't be blank" on field "competition"$/) do
30
+ team = TeamPage.find
31
+ expect(team.errors['competition']).to eq ['can't be blank']
32
+ end
33
+ ```
34
+
35
+ ###How to get the right HTML?
36
+ PageRecord contains form builders that make it extremely easy to build the right forms. When you use Formtastic. You don't have to do anything. PageRecord add's the right attributes to the right DOM elements. When you use barebone Rails, you van use the `record_form_for` form builder. See the example for more information.
11
37
 
12
38
  ##Documentation
13
39
  Look at [the yard documentation](http://rubydoc.info/github/appdrones/page_record/PageRecord) for details. Check [Changes](https://github.com/appdrones/page_record/blob/master/CHANGES.md) for (breaking) changes per version.
data/bin/autospec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'autospec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'autospec')
data/bin/guard ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'guard' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('guard', 'guard')
data/bin/rake ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rake' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rake', 'rake')
data/bin/rspec ADDED
@@ -0,0 +1,16 @@
1
+ #!/usr/bin/env ruby
2
+ #
3
+ # This file was generated by Bundler.
4
+ #
5
+ # The application 'rspec' is installed as part of a gem, and
6
+ # this file is here to facilitate running it.
7
+ #
8
+
9
+ require 'pathname'
10
+ ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
11
+ Pathname.new(__FILE__).realpath)
12
+
13
+ require 'rubygems'
14
+ require 'bundler/setup'
15
+
16
+ load Gem.bin_path('rspec-core', 'rspec')
@@ -1,60 +1,63 @@
1
1
  module PageRecord
2
- class Base
3
- ##
4
- # Searches the record for the specified attribute and returns the {http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Result Capybara Result}.
5
- # This method is called when you access an attribute with a `?` of a record
6
- #
7
- # @return [Capybara::Result] the text content of the specified attribute
8
- #
9
- # @raise [AttributeNotFound] when the attribute is not found in the record
10
- #
11
- def read_attribute?(attribute)
12
- begin
13
- @record.find("[data-attribute-for='#{attribute}']")
14
- rescue Capybara::ElementNotFound
15
- raise AttributeNotFound, "#{@type} record with id #{@id} doesn't contain attribute #{attribute}"
16
- end
17
- end
2
+ class Base
3
+ ##
4
+ # Searches the record for the specified attribute and returns
5
+ # the {http://rubydoc.info/github/jnicklas/capybara/master/Capybara/Result Capybara Result}.
6
+ # This method is called when you access an attribute with a `?` of a record
7
+ #
8
+ # @return [Capybara::Result] the text content of the specified attribute
9
+ #
10
+ # @raise [AttributeNotFound] when the attribute is not found in the record
11
+ #
12
+ def read_attribute?(attribute)
13
+ @record.find("[data-attribute-for='#{attribute}']")
14
+ rescue Capybara::ElementNotFound
15
+ raise AttributeNotFound, "#{@type} record with id #{@id} doesn't contain attribute #{attribute}"
16
+ end
18
17
 
19
- ##
20
- # Searches the record for the specified attribute and returns the text content.
21
- # This method is called when you access an attribute of a record
22
- #
23
- # @return [String] the text content of the specified attribute
24
- #
25
- # @raise [AttributeNotFound] when the attribute is not found in the record
26
- #
27
- def read_attribute( attribute)
28
- element = self.send("#{attribute}?")
29
- tag = element.tag_name
30
- textelement?(tag) ? element.value : element.text
31
- end
18
+ ##
19
+ # Searches the record for the specified attribute and returns
20
+ # the text content. This method is called when you access an
21
+ # attribute of a record
22
+ #
23
+ # @return [String] the text content of the specified attribute
24
+ #
25
+ # @raise [AttributeNotFound] when the attribute is not found in the record
26
+ #
27
+ def read_attribute(attribute)
28
+ element = send("#{attribute}?")
29
+ tag = element.tag_name
30
+ input_field?(tag) ? element.value : element.text
31
+ end
32
32
 
33
- ##
34
- # Searches the record for the specified attribute and sets the value of the attribute
35
- # This method is called when you set an attribute of a record
36
- #
37
- # @return [Capybara::Result] the text content of the specified attribute
38
- #
39
- # @raise [AttributeNotFound] when the attribute is not found in the record
40
- # @raise [NotInputField] when the attribute is not a `TEXTAREA` or `INPUT` tag
41
- #
42
- def write_attribute( attribute, value)
43
- element = self.send("#{attribute}?")
44
- tag = element.tag_name
45
- raise NotInputField unless textelement?(tag)
46
- element.set(value)
47
- element
48
- end
33
+ ##
34
+ # Searches the record for the specified attribute and sets the value of the attribute
35
+ # This method is called when you set an attribute of a record
36
+ #
37
+ # @return [Capybara::Result] the text content of the specified attribute
38
+ #
39
+ # @raise [AttributeNotFound] when the attribute is not found in the record
40
+ # @raise [NotInputField] when the attribute is not a `TEXTAREA` or `INPUT` tag
41
+ #
42
+ def write_attribute(attribute, value)
43
+ element = send("#{attribute}?")
44
+ tag = element.tag_name
45
+ case tag
46
+ when 'textarea', 'input' then element.set(value)
47
+ when 'select'then element.select(value)
48
+ else raise NotInputField
49
+ end
50
+ element
51
+ end
49
52
 
53
+ private
50
54
 
51
- private
52
- # @private
53
- def textelement?(tag)
54
- case tag
55
- when 'textarea', 'input' then true
56
- else false
57
- end
58
- end
59
- end
55
+ # @private
56
+ def input_field?(tag)
57
+ case tag
58
+ when 'textarea', 'input', 'select' then true
59
+ else false
60
+ end
61
+ end
62
+ end
60
63
  end
@@ -1,29 +1,34 @@
1
1
  module PageRecord
2
2
 
3
- class Base
3
+ class Base
4
+ include PageRecord::Inspector
4
5
 
5
- attr_reader :id
6
- alias :id? :id
6
+ attr_reader :id
7
+ alias_method :id?, :id
7
8
 
8
- def initialize(id=nil, selector=nil, filter=nil)
9
- selector ||= self.instance_variable_get('@selector')
10
- filter ||= self.instance_variable_get('@filter')
11
- @page = self.class.page
12
- # raise PageNotSet, "page variable not set" unless @page
13
- @type = self.class.instance_variable_get('@type')
14
- @id = id.to_s
15
- id_text = @id.blank? ? "" : "='#{@id}'"
16
- begin
17
- context = self.class.context_for_selector(selector)
18
- @record = context.find("[data-#{@type}-id#{id_text}]#{filter}")
19
- @id = @record["data-#{@type}-id"] if @id.blank?
20
- rescue Capybara::Ambiguous
21
- raise MultipleRecords, "Found multiple #{@type} record with id #{@id} on page"
22
- rescue Capybara::ElementNotFound
23
- raise RecordNotFound, "#{@type} record with id #{@id} not found on page"
24
- end
25
- end
9
+ def initialize(id = nil, selector = nil, filter = nil)
10
+ @page = self.class.page
11
+ @type = self.class.instance_variable_get('@type')
12
+ @id = id.to_s
13
+ find_record(selector, filter)
14
+ end
15
+
16
+ private
17
+
18
+ def find_record(selector, filter)
19
+ selector ||= @selector
20
+ filter ||= @filter
21
+ id_text = @id.blank? ? '' : "='#{@id}'"
22
+ begin
23
+ context = self.class.context_for_selector(selector)
24
+ @record = context.find("[data-#{@type}-id#{id_text}]#{filter}")
25
+ @id = @record["data-#{@type}-id"] if @id.blank?
26
+ rescue Capybara::Ambiguous
27
+ raise MultipleRecords, "Found multiple #{@type} record with id #{@id} on page"
28
+ rescue Capybara::ElementNotFound
29
+ raise RecordNotFound, "#{@type} record with id #{@id} not found on page"
30
+ end
31
+ end
26
32
 
27
33
  end
28
34
  end
29
-
@@ -1,55 +1,52 @@
1
1
  module PageRecord
2
- class Base
3
- ##
4
- #
5
- # This is the implementation of the page action routine. It has two variants.
6
- # it has a `?` variant and a `normal` variant.
7
- #
8
- # normal variant:
9
- # It checks the page for a data-action-for='action' tag somewhere on the page.
10
- # If it finds it, it clicks it.
11
- #
12
- # `?` variant:
13
- # It checks the page for a data-action-for='action' tag somewhere on the page.
14
- # If it finds it, returns the Capybara result.
15
- #
16
- # @param action [Symbol] this is the name of the action
17
- #
18
- # @return [Capybara::Element]
19
- #
20
- # @raise [PageRecord::MultipleRecords] when there are more actions with
21
- # this name on the page
22
- #
23
- def self.method_missing(action)
24
- raw_action = /(.*)\?/.match(action)
25
- begin
26
- if raw_action
27
- action_for?(raw_action[1])
28
- else
29
- action_for(action)
30
- end
31
- rescue Capybara::Ambiguous
32
- raise MultipleRecords, "Found multiple #{action} tags for #{@type} on page"
33
- rescue Capybara::ElementNotFound
34
- super
35
- end
36
- end
2
+ class Base
3
+ ##
4
+ #
5
+ # This is the implementation of the page action routine. It has two variants.
6
+ # it has a `?` variant and a `normal` variant.
7
+ #
8
+ # normal variant:
9
+ # It checks the page for a data-action-for='action' tag somewhere on the page.
10
+ # If it finds it, it clicks it.
11
+ #
12
+ # `?` variant:
13
+ # It checks the page for a data-action-for='action' tag somewhere on the page.
14
+ # If it finds it, returns the Capybara result.
15
+ #
16
+ # @param action [Symbol] this is the name of the action
17
+ #
18
+ # @return [Capybara::Element]
19
+ #
20
+ # @raise [PageRecord::MultipleRecords] when there are more actions with
21
+ # this name on the page
22
+ #
23
+ def self.method_missing(action)
24
+ raw_action = /(.*)\?/.match(action)
25
+ begin
26
+ if raw_action
27
+ action_for?(raw_action[1])
28
+ else
29
+ action_for(action)
30
+ end
31
+ rescue Capybara::Ambiguous
32
+ raise MultipleRecords, "Found multiple #{action} tags for #{@type} on page"
33
+ rescue Capybara::ElementNotFound
34
+ super
35
+ end
36
+ end
37
37
 
38
- private
38
+ private
39
39
 
40
- # @private
41
- def self.action_for(action)
42
- element = action_for?(action)
43
- element.click
44
- element
45
- end
40
+ # @private
41
+ def self.action_for(action)
42
+ element = action_for?(action)
43
+ element.click
44
+ element
45
+ end
46
46
 
47
-
48
- # @private
49
- def self.action_for?(action)
50
- page.find("[data-action-for='#{action}']")
51
- end
52
- end
47
+ # @private
48
+ def self.action_for?(action)
49
+ page.find("[data-action-for='#{action}']")
50
+ end
51
+ end
53
52
  end
54
-
55
-