page_record 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,88 @@
1
+ module PageRecord
2
+
3
+
4
+ ##
5
+ # This {::Exception} is raised when the specified record is not found
6
+ # on the page. Check your selector, filter and HTML code for details.
7
+ #
8
+ # ```html
9
+ # <div data-team-id='10'>
10
+ # <div data-attribute-for='name'>Ajax</div>
11
+ # </div>
12
+ # ```
13
+ # When the following code is executed, the {RecordNotFound} exception is raised.
14
+ #
15
+ # ```ruby
16
+ # TeamPage.find(11)
17
+ #```
18
+ #
19
+ class RecordNotFound < Exception
20
+ end
21
+
22
+ ##
23
+ # This {::Exception} is raised when the specfied attribute is not found
24
+ # on the page. Check your selector, filter and HTML code for details.
25
+ #
26
+ # ```html
27
+ # <div data-team-id='10'>
28
+ # <div data-attribute-for='name'>Ajax</div>
29
+ # </div>
30
+ # ```
31
+ # When the following code is executed, the {AttributeNotFound} exception is raised.
32
+ #
33
+ # ```ruby
34
+ # TeamPage.find(10).ranking
35
+ #```
36
+ #
37
+ class AttributeNotFound < Exception
38
+ end
39
+
40
+ ##
41
+ # This {::Exception} is raised when you have not set the page variable
42
+ # of the class. Check {PageRecord::PageRecord.page} for details
43
+ #
44
+ class PageNotSet < Exception
45
+ end
46
+
47
+ ##
48
+ # This {::Exception} is raised when the page contains multiple instances
49
+ # of the specfied record type. Use a selector to narrow the search.
50
+ #
51
+ # ```html
52
+ # <div id='first-table' data-team-id='10'>
53
+ # <div data-attribute-for='name'>Ajax</div>
54
+ # </div>
55
+ # <div id='second-table' data-team-id='10'>
56
+ # <div data-attribute-for='name'>Ajax</div>
57
+ # </div>
58
+ # ```
59
+ # When the following code is executed, the {MultipleRecords} exception is raised.
60
+ #
61
+ # ```ruby
62
+ # TeamPage.find(10)
63
+ #```
64
+ #
65
+ # To fix this, use the `#first-table` in the selector
66
+ #
67
+ # ```ruby
68
+ # TeamPage.find(10, '#first-table')
69
+ #```
70
+ #
71
+ class MultipleRecords < Exception
72
+ end
73
+
74
+ # This {::Exception} is raised when you try to set a non input field.
75
+ #
76
+ # ```ruby
77
+ # <div data-team-id=1>
78
+ # <div data-attribute-for='name'>PSV</div>
79
+ # </div>
80
+ # ```
81
+ # When the following code is executed, the {NotInputField} exception is raised.
82
+ #
83
+ # ```ruby
84
+ # team_on_page.name = 'Ajax'
85
+ # ```
86
+ class NotInputField < Exception
87
+ end
88
+ end
@@ -0,0 +1,135 @@
1
+ module PageRecord
2
+ ##
3
+ # PageRecord is a specific sort of {http://assertselenium.com/automation-design-practices/page-object-pattern/ PageObject pattern}. Where a "normal"
4
+ # {http://assertselenium.com/automation-design-practices/page-object-pattern/ PageObject} tries to make the page accessible with business like functions.
5
+ # We have taken a different approach. We've noticed that a lot of WebPage are
6
+ # mainly luxury CRUD pages. This means that almost every page shows one or more
7
+ # records of a certain type and has the ability to create, read update and delete
8
+ # one or more records. Sounds familiar? Yes, it is the same as an ActiveRecord
9
+ # pattern. So we tried to make accessing a page as close as possible to accessing
10
+ # an ActiveRecord.
11
+ #
12
+ # To make this work, however, we need to add extra information to the HTML page.
13
+ # With HTML5, , you can do this easily. HTML-5 supports the data- attributes on any
14
+ # tag. We use these tags to identify the records on the page.
15
+ #
16
+ #
17
+ class PageRecord
18
+ ##
19
+ #
20
+ # Searches the page and returns an {::Array} of {PageRecord::PageRecord} of instances of
21
+ # that match the selector and the filter. See {file:README.md#markup markup} for more
22
+ # details about formatting the page.
23
+ #
24
+ # example:
25
+ #
26
+ # ```ruby
27
+ # TeamPage.all
28
+ # ```
29
+ #
30
+ # returns all records on the page
31
+ #
32
+ # @param selector [String] selector to use for searching the table on the page
33
+ # @param filter [String] filter to use on the records on the page
34
+ #
35
+ # @return [Array Pagerecord] The Array containing instances of [PageRecord::PageRecord]
36
+ # with records that fit the selector and the filter
37
+ #
38
+ # @raise [MultipleRecords] if the page contains more then on set of records
39
+ # @raise [RecordNotFound] if the page does not contain any of the specified records
40
+ #
41
+ def self.all(selector = nil, filter = nil)
42
+ selector ||= self.instance_variable_get('@selector')
43
+ filter ||= self.instance_variable_get('@filter')
44
+ records = []
45
+ context = context_for_selector(selector)
46
+ context.all("[data-#{@type}-id]#{filter}").each do | record|
47
+ id = record["data-#{@type}-id"]
48
+ records << self.new(id, selector)
49
+ end
50
+ records
51
+ end
52
+
53
+ ##
54
+ #
55
+ # Searches the page and returns an instance of {PageRecord::PageRecord} of instances of
56
+ # that matches the given id, selector and the filter. See {file:README.md#markup markup} for more
57
+ # details about formatting the page.
58
+ #
59
+ # example:
60
+ #
61
+ # ```ruby
62
+ # TeamPage.find(1)
63
+ # ```
64
+ #
65
+ # returns the record with id
66
+ #
67
+ # When you don't specify an id, `find` returns the only record on the page.
68
+ # If you have more than one record on the page, `find` raises {MultipleRecords}.
69
+ #
70
+ # example:
71
+ #
72
+ # ```ruby
73
+ # TeamPage.find()
74
+ # ```
75
+ #
76
+ # @param selector [String] selector to use for searching the table on the page
77
+ # @param filter [String] filter to use on the records on the page
78
+ #
79
+ # @return [Pagerecord] An instance of [PageRecord::PageRecord]
80
+ #
81
+ # @raise [MultipleRecords] if the page contains more then on set of records
82
+ # @raise [RecordNotFound] if the page does not contain any of the specified records
83
+ #
84
+ def self.find(id=nil, selector = nil, filter= nil)
85
+ selector ||= self.instance_variable_get('@selector')
86
+ filter ||= self.instance_variable_get('@filter')
87
+ self.new(id, selector, filter)
88
+ end
89
+
90
+ ##
91
+ #
92
+ # Searches the page and returns an instance of {PageRecord::PageRecord} of instances of
93
+ # that matches the given attribute. See {file:README.md#markup markup} for more
94
+ # details about formatting the page.
95
+ #
96
+ # Although you can call this yourself, {PageRecord::PageRecord} uses this method for defining a
97
+ # finder for all attributes when you define your page class, {PageRecord::PageRecord}
98
+ # See {PageRecord::PageRecord.attributes} for more details.
99
+ #
100
+ # example:
101
+ #
102
+ # ```ruby
103
+ # TeamPage.find_by_name('Ajax')
104
+ # ```
105
+ #
106
+ # returns the record where the name is set to Ajax
107
+ #
108
+ # @param attribute [String] The attribute name
109
+ # @param value [String] The value to search for
110
+ # @param selector [String] selector to use for searching the table on the page
111
+ # @param filter [String] filter to use on the records on the page
112
+ #
113
+ # @return [Pagerecord] An instance of [PageRecord::PageRecord].
114
+ #
115
+ # @raise [MultipleRecords] if the page contains more then on set of records
116
+ # @raise [RecordNotFound] if the page does not contain any of the specified records
117
+ #
118
+ def self.find_by_attribute(attribute, value, selector, filter)
119
+ begin
120
+ selector ||= self.instance_variable_get('@selector')
121
+ filter ||= self.instance_variable_get('@filter')
122
+
123
+ context = self.context_for_selector(selector)
124
+ record = context.find("[data-#{@type}-id]#{filter} > [data-attribute-for='#{attribute}']", :text => value)
125
+ parent = record.find(:xpath, "..")
126
+ id = parent["data-#{@type}-id"]
127
+ self.new(id, selector)
128
+ rescue Capybara::Ambiguous
129
+ raise MultipleRecords, "Found multiple #{@type} record with #{attribute} #{value} on page"
130
+ rescue Capybara::ElementNotFound
131
+ raise RecordNotFound, "#{@type} record with #{attribute} #{value} not found on page"
132
+ end
133
+ end
134
+ end
135
+ end
@@ -0,0 +1,52 @@
1
+ module PageRecord
2
+ class PageRecord
3
+
4
+ ##
5
+ # This is the implementation of the record 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 element.
15
+ #
16
+ # @param action [Symbol] this is the name of the action
17
+ #
18
+ # @return [Capybara::Result]
19
+ #
20
+ # @raise [PageRecord::MultipleRecords] when there are more actions with
21
+ # this name on the page
22
+ #
23
+ def 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::ElementNotFound
32
+ super
33
+ end
34
+ end
35
+
36
+
37
+ private
38
+
39
+ # @private
40
+ def action_for(action)
41
+ element = action_for?(action)
42
+ element.click
43
+ element
44
+ end
45
+
46
+ # @private
47
+ def action_for?(action)
48
+ @record.find("[data-action-for='#{action}']")
49
+ end
50
+ end
51
+ end
52
+
@@ -0,0 +1,29 @@
1
+
2
+ module PageRecord
3
+
4
+ class PageRecord
5
+
6
+ attr_reader :id
7
+ alias :id? :id
8
+
9
+ def initialize(id=nil, selector=nil, filter=nil)
10
+ selector ||= self.instance_variable_get('@selector')
11
+ filter ||= self.instance_variable_get('@filter')
12
+ @page = self.class.page
13
+ # raise PageNotSet, "page variable not set" unless @page
14
+ @type = self.class.instance_variable_get('@type')
15
+ @id = id.to_s
16
+ id_text = @id.blank? ? "" : "='#{@id}'"
17
+ begin
18
+ context = self.class.context_for_selector(selector)
19
+ @record = context.find("[data-#{@type}-id#{id_text}]#{filter}")
20
+ @id = @record["data-#{@type}-id"] if @id.blank?
21
+ rescue Capybara::Ambiguous
22
+ raise MultipleRecords, "Found multiple #{@type} record with id #{@id} on page"
23
+ rescue Capybara::ElementNotFound
24
+ raise RecordNotFound, "#{@type} record with id #{@id} not found on page"
25
+ end
26
+ end
27
+
28
+ end
29
+ end
@@ -0,0 +1,7 @@
1
+ require 'page_record'
2
+
3
+ RSpec.configure do |config|
4
+ config.before(:each) do
5
+ PageRecord::PageRecord.page = session
6
+ end
7
+ end
@@ -0,0 +1,3 @@
1
+ module PageRecord
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,9 @@
1
+ require 'active_support/core_ext'
2
+ require "page_record/version"
3
+ require "page_record/page_record"
4
+ require "page_record/finders"
5
+ require "page_record/instance_actions"
6
+ require "page_record/attribute_accessors"
7
+ require "page_record/class_actions"
8
+ require "page_record/class_methods"
9
+ require "page_record/errors"
@@ -0,0 +1,29 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'page_record/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "page_record"
8
+ spec.version = PageRecord::VERSION
9
+ spec.authors = ["Bert Hajee"]
10
+ spec.email = ["hajee@moretIA.com"]
11
+ spec.description = %q{ActiveRecord like reading from specialy formatted HTML-page}
12
+ spec.summary = %q{Using some specialy formatted 'data-...' tags you can read records from HTML pages like an ActiveRecord page}
13
+ spec.homepage = "https://github.com/appdrones/page_record"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files`.split($/)
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.3"
22
+ spec.add_development_dependency "rake"
23
+ spec.add_development_dependency "rspec"
24
+ spec.add_development_dependency "sinatra"
25
+ # spec.add_development_dependency "debugger"
26
+
27
+ spec.add_dependency "capybara" , '~>2.1.0'
28
+ spec.add_dependency "activesupport"
29
+ end