page_magic 1.0.0.alpha17 → 1.0.0.alpha18

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: b6aee6a0833a50dbf849f13b2c7a4c3668feef1d
4
- data.tar.gz: b4d0aecfbba7509a3ffb9e2bf08b38cb30f4cb64
3
+ metadata.gz: 57c321555c03ce3b8a6a43d895ffd657eca3c5ae
4
+ data.tar.gz: f656f163ed661b0b48ffbc5b2b2d30f5db777beb
5
5
  SHA512:
6
- metadata.gz: 4437e58a2107ffcec212ee3778450b73f5384bc46581c78b53c8f28e2c69d3a5c42731c1a8356ff1c42e2ccdb7ba5b4af0c68c6de62477e34349729b9fbc13fd
7
- data.tar.gz: 27e427b3fa2bc153836c545b24ef9ea3cf14e360eba3acc3f7fb932ff2c694a6262746735d60eb4752749f0c9cb335bf55c8ff82d4a4ea0f0fe0517f91e5df2d
6
+ metadata.gz: 4cd81bb246ed5a267265db1ab31d8861a772c9fa54ac000e0d5dd4ac9340a180a0c047876fe28bd2cbf3a58e29840b342f221c15f1f13564f6486dcc0b1e2806
7
+ data.tar.gz: 8957aa92ef4ae0dc088fed4d4bf7d13feae4118064b12270add171fe52f0fda423fa8787fcef5b1112b526171bc308676bdc2d9f40c79502da928250b1c8a3a1
data/.codeclimate.yml ADDED
@@ -0,0 +1,4 @@
1
+ languages:
2
+ Ruby: true
3
+ exclude_paths:
4
+ - "spec/*"
data/.pullreview.yml ADDED
@@ -0,0 +1,14 @@
1
+ ---
2
+ languages:
3
+ - ruby
4
+ rules:
5
+ test_infection:
6
+ except:
7
+ - 'lib/page_magic/exceptions*'
8
+ missing_class_documentation:
9
+ except:
10
+ - 'lib/page_magic/element/query.rb'
11
+ - 'lib/page_magic/element/selector.rb'
12
+ - 'lib/page_magic/element/method_observer.rb'
13
+ - 'lib/page_magic/element.rb'
14
+ - 'lib/page_magic/session.rb'
data/.rubocop.yml CHANGED
@@ -1,6 +1,3 @@
1
- Documentation:
2
- Enabled: false
3
-
4
1
  Metrics/LineLength:
5
2
  Max: 120
6
3
 
data/.simplecov CHANGED
@@ -1,3 +1,8 @@
1
+ require 'pullreview/coverage'
2
+ require "codeclimate-test-reporter"
3
+
4
+ SimpleCov.formatters = [SimpleCov::Formatter::HTMLFormatter,PullReview::Coverage::Formatter,CodeClimate::TestReporter::Formatter]
5
+
1
6
  SimpleCov.start do
2
7
  add_filter '/spec/'
3
8
  end
data/.yardopts ADDED
@@ -0,0 +1,5 @@
1
+ --no-private
2
+ lib
3
+ --markup-provider=redcarpet
4
+ --markup=markdown
5
+ --readme README.md
data/Gemfile CHANGED
@@ -10,9 +10,15 @@ group :test do
10
10
  gem 'rspec', require: 'rspec/core/rake_task'
11
11
  gem 'simplecov', require: false
12
12
  gem 'poltergeist'
13
+ gem 'codeclimate-test-reporter', group: :test, require: nil
14
+ gem 'pullreview-coverage', require: false
13
15
  end
14
16
 
15
17
  group :development do
16
18
  gem 'jeweler'
17
19
  gem 'rubocop', require: 'rubocop/rake_task'
20
+ gem 'pry-byebug'
21
+ gem 'yard'
22
+ gem 'redcarpet'
23
+ gem 'github-markup'
18
24
  end
data/Gemfile.lock CHANGED
@@ -12,20 +12,29 @@ GEM
12
12
  astrolabe (1.3.1)
13
13
  parser (~> 2.2)
14
14
  builder (3.2.2)
15
+ byebug (5.0.0)
16
+ columnize (= 0.9.0)
15
17
  capybara (2.1.0)
16
18
  mime-types (>= 1.16)
17
19
  nokogiri (>= 1.3.3)
18
20
  rack (>= 1.0.0)
19
21
  rack-test (>= 0.5.4)
20
22
  xpath (~> 2.0)
23
+ certifi (2015.08.10)
21
24
  childprocess (0.3.9)
22
25
  ffi (~> 1.0, >= 1.0.11)
23
26
  cliver (0.2.2)
27
+ codeclimate-test-reporter (0.4.8)
28
+ simplecov (>= 0.7.1, < 1.0.0)
29
+ coderay (1.1.0)
30
+ columnize (0.9.0)
24
31
  diff-lcs (1.2.5)
32
+ docile (1.1.5)
25
33
  faraday (0.8.9)
26
34
  multipart-post (~> 1.2.0)
27
35
  ffi (1.9.3)
28
36
  git (1.2.6)
37
+ github-markup (1.4.0)
29
38
  github_api (0.10.2)
30
39
  addressable
31
40
  faraday (~> 0.8.7)
@@ -48,10 +57,11 @@ GEM
48
57
  json (1.8.1)
49
58
  jwt (0.1.11)
50
59
  multi_json (>= 1.5)
60
+ method_source (0.8.2)
51
61
  mime-types (1.25)
52
62
  mini_portile (0.5.1)
53
63
  minitest (5.7.0)
54
- multi_json (1.7.3)
64
+ multi_json (1.11.2)
55
65
  multi_xml (0.5.5)
56
66
  multipart-post (1.2.0)
57
67
  nokogiri (1.6.0)
@@ -70,6 +80,16 @@ GEM
70
80
  multi_json (~> 1.0)
71
81
  websocket-driver (>= 0.2.0)
72
82
  powerpack (0.1.1)
83
+ pry (0.10.3)
84
+ coderay (~> 1.1.0)
85
+ method_source (~> 0.8.1)
86
+ slop (~> 3.4)
87
+ pry-byebug (3.2.0)
88
+ byebug (~> 5.0)
89
+ pry (~> 0.10)
90
+ pullreview-coverage (0.0.5)
91
+ certifi
92
+ simplecov (>= 0.7.1, < 1.0.0)
73
93
  rack (1.5.2)
74
94
  rack-protection (1.5.0)
75
95
  rack
@@ -79,6 +99,7 @@ GEM
79
99
  rake (10.2.2)
80
100
  rdoc (4.1.1)
81
101
  json (~> 1.4)
102
+ redcarpet (3.3.3)
82
103
  rspec (3.3.0)
83
104
  rspec-core (~> 3.3.0)
84
105
  rspec-expectations (~> 3.3.0)
@@ -105,14 +126,16 @@ GEM
105
126
  multi_json (~> 1.0)
106
127
  rubyzip (< 1.0.0)
107
128
  websocket (~> 1.0.4)
108
- simplecov (0.7.1)
109
- multi_json (~> 1.0)
110
- simplecov-html (~> 0.7.1)
111
- simplecov-html (0.7.1)
129
+ simplecov (0.10.0)
130
+ docile (~> 1.1.0)
131
+ json (~> 1.8)
132
+ simplecov-html (~> 0.10.0)
133
+ simplecov-html (0.10.0)
112
134
  sinatra (1.4.3)
113
135
  rack (~> 1.4)
114
136
  rack-protection (~> 1.4)
115
137
  tilt (~> 1.3, >= 1.3.4)
138
+ slop (3.6.0)
116
139
  thread_safe (0.3.5)
117
140
  tilt (1.4.1)
118
141
  tzinfo (1.2.2)
@@ -124,6 +147,7 @@ GEM
124
147
  websocket-driver (0.3.0)
125
148
  xpath (2.0.0)
126
149
  nokogiri (~> 1.3)
150
+ yard (0.8.7.6)
127
151
 
128
152
  PLATFORMS
129
153
  ruby
@@ -131,11 +155,17 @@ PLATFORMS
131
155
  DEPENDENCIES
132
156
  activesupport
133
157
  capybara
158
+ codeclimate-test-reporter
159
+ github-markup
134
160
  jeweler
135
161
  poltergeist
162
+ pry-byebug
163
+ pullreview-coverage
164
+ redcarpet
136
165
  rspec
137
166
  rubocop
138
167
  simplecov
139
168
  sinatra
140
169
  wait
141
170
  watir-webdriver
171
+ yard
data/README.md CHANGED
@@ -1,3 +1,4 @@
1
+ [![Code Climate](https://codeclimate.com/github/Ladtech/page_magic/badges/gpa.svg)](https://codeclimate.com/github/Ladtech/page_magic) [![Test Coverage](https://codeclimate.com/github/Ladtech/page_magic/badges/coverage.svg)](https://codeclimate.com/github/Ladtech/page_magic/coverage) [![PullReview stats](https://www.pullreview.com/github/Ladtech/page_magic/badges/master.svg?)](https://www.pullreview.com/github/Ladtech/page_magic/reviews/master)
1
2
  #PageMagic
2
3
  PageMagic is an API for testing web applications.
3
4
 
@@ -33,7 +34,7 @@ What we really want to write is something like
33
34
  ```ruby
34
35
  test_subject = send_test_mail('test@21st-century-mail.com')
35
36
  #Visit your site using a PageMagic session we prepared earlier
36
- session.visit(LoginPage, url: 'https://21st-century-mail.com')
37
+ session.visit(LoginPage)
37
38
 
38
39
  #Login using some handy helper method on our page object
39
40
  session.login('username', 'password')
@@ -51,7 +52,7 @@ fail "message is still there!" if session.message(subject: test_subject).exists?
51
52
  ## Starting a session
52
53
  To start a PageMagic session simply decide what browser you want to use and pass it to PageMagic's `.session` method
53
54
  ```ruby
54
- session = PageMagic.session(browser: :chrome)
55
+ session = PageMagic.session(browser: :chrome, url: 'https://21st-century-mail.com')
55
56
  ```
56
57
  Out of the box, PageMagic knows how to work with:
57
58
  - Chrome and Firefox
@@ -196,7 +197,7 @@ end
196
197
  PageMagic.drivers.register Webkit
197
198
 
198
199
  #3. Use registered driver
199
- session = PageMagic.session(browser: webkit)
200
+ session = PageMagic.session(browser: webkit, url: 'https://21st-century-mail.com')
200
201
  ```
201
202
  ##What else can you do with PageMagic?
202
203
  PageMagic has lots of other useful features. I'm writing up the documentation so check back here soon!
data/VERSION CHANGED
@@ -1 +1 @@
1
- 1.0.0.alpha17
1
+ 1.0.0.alpha18
data/circle.yml ADDED
@@ -0,0 +1,3 @@
1
+ test:
2
+ override:
3
+ - bundle exec rake spec
@@ -1,4 +1,5 @@
1
1
  module PageMagic
2
+ # class Driver - instances are factories for drivers used by PageMagic
2
3
  class Driver
3
4
  attr_reader :supported_browsers, :handler
4
5
  def initialize(*supported_browsers, &block)
@@ -1,8 +1,4 @@
1
- module PageMagic
2
- class Drivers
3
- Poltergeist = Driver.new(:poltergeist) do |app, options|
4
- require 'capybara/poltergeist'
5
- Capybara::Poltergeist::Driver.new(app, options)
6
- end
7
- end
1
+ PageMagic::Drivers::Poltergeist = PageMagic::Driver.new(:poltergeist) do |app, options|
2
+ require 'capybara/poltergeist'
3
+ Capybara::Poltergeist::Driver.new(app, options)
8
4
  end
@@ -1,7 +1,3 @@
1
- module PageMagic
2
- class Drivers
3
- RackTest = Driver.new(:rack_test) do |app, options|
4
- Capybara::RackTest::Driver.new(app, options)
5
- end
6
- end
1
+ PageMagic::Drivers::RackTest = PageMagic::Driver.new(:rack_test) do |app, options|
2
+ Capybara::RackTest::Driver.new(app, options)
7
3
  end
@@ -1,8 +1,4 @@
1
- module PageMagic
2
- class Drivers
3
- Selenium = Driver.new(:chrome, :firefox) do |app, options, browser|
4
- require 'watir-webdriver'
5
- Capybara::Selenium::Driver.new(app, options.dup.merge(browser: browser))
6
- end
7
- end
1
+ PageMagic::Drivers::Selenium = PageMagic::Driver.new(:chrome, :firefox) do |app, options, browser|
2
+ require 'watir-webdriver'
3
+ Capybara::Selenium::Driver.new(app, options.dup.merge(browser: browser))
8
4
  end
@@ -1,5 +1,7 @@
1
1
  require 'page_magic/driver'
2
2
  module PageMagic
3
+ # class Drivers - creates an object that can be used to hold driver definitions
4
+ # These PageMagic gets the user's chosen driver from this object.
3
5
  class Drivers
4
6
  def all
5
7
  @all ||= []
@@ -13,6 +15,8 @@ module PageMagic
13
15
  all.find { |driver| driver.support?(browser) }
14
16
  end
15
17
 
18
+ # Loads drivers defined in files at the given path
19
+ # @param [String] path where the drivers are located
16
20
  def load(path = "#{__dir__}/drivers")
17
21
  require 'active_support/inflector'
18
22
 
@@ -1,10 +1,17 @@
1
1
  module PageMagic
2
2
  class Element
3
+ # module MethodObserver - adds methods to check if a methods have been added.
3
4
  module MethodObserver
5
+ # Hook called by ruby when a singleton method is added.
6
+ #
7
+ # @param [String] arg name of the method added
4
8
  def singleton_method_added(arg)
5
9
  @singleton_methods_added = true unless arg == :singleton_method_added
6
10
  end
7
11
 
12
+ # returns true if a singleton method has been added
13
+ #
14
+ # @return [Boolean]
8
15
  def singleton_methods_added?
9
16
  @singleton_methods_added == true
10
17
  end
@@ -4,9 +4,11 @@ module PageMagic
4
4
  # - requirements on element type
5
5
  # - selection criteria, modeled through the Selector class
6
6
  # - options
7
-
8
7
  class Query
9
8
  class << self
9
+ # Find a query using it's name
10
+ # @param [Symbol] type the name of the required query in snakecase format
11
+ # @return [Query] returns the predefined query with the given name
10
12
  def find(type)
11
13
  query = constants.find { |constant| constant.to_s.downcase == type.to_s.downcase }
12
14
  return ELEMENT unless query
@@ -21,6 +23,10 @@ module PageMagic
21
23
  @type = type
22
24
  end
23
25
 
26
+ # Build query parameters for Capybara's find method
27
+ # @param [Hash] locator the location method e.g. text: 'button text'
28
+ # @param [Hash] options additional options to be provided to Capybara. e.g. count: 3
29
+ # @return [Array] list of compatible capybara query parameters.
24
30
  def build(locator, options = {})
25
31
  [].tap do |array|
26
32
  selector = Selector.find(locator.keys.first)
@@ -3,6 +3,10 @@ module PageMagic
3
3
  # class Selector - models the selection criteria understood by Capybara
4
4
  class Selector
5
5
  class << self
6
+ # Find a Selecor using it's name
7
+ # @param [Symbol] name the name of the required Selector in snakecase format. See class constants for available
8
+ # selectors
9
+ # @return [Selector] returns the predefined selector with the given name
6
10
  def find(name)
7
11
  selector = constants.find { |constant| constant.to_s.downcase == name.to_s.downcase }
8
12
  fail UnsupportedCriteriaException unless selector
@@ -10,6 +14,9 @@ module PageMagic
10
14
  end
11
15
  end
12
16
 
17
+ # Build selector query parameters for Capybara's find method
18
+ # @param [Symbol] element_type the type of browser element being found. e.g :link
19
+ # @param [Hash] locator the selection method and its parameter. E.g. text: 'click me'
13
20
  def build(element_type, locator)
14
21
  [].tap do |array|
15
22
  array << element_type if supports_type
@@ -1,5 +1,6 @@
1
1
  module PageMagic
2
2
  class Element
3
+ # module SelectorMethods - adds method for getting and setting an element selector
3
4
  module SelectorMethods
4
5
  def selector(selector = nil)
5
6
  return @selector unless selector
@@ -3,19 +3,15 @@ require 'page_magic/element/selector_methods'
3
3
  require 'page_magic/element/selector'
4
4
  require 'page_magic/element/query'
5
5
  module PageMagic
6
+ # class Element - represents an element in a html page.
6
7
  class Element
7
8
  EVENT_TYPES = [:set, :select, :select_option, :unselect_option, :click]
8
9
  DEFAULT_HOOK = proc {}.freeze
10
+
9
11
  attr_reader :type, :name, :parent_page_element, :browser_element
10
12
 
11
13
  include Elements, MethodObserver, SelectorMethods
12
- extend SelectorMethods
13
-
14
- class << self
15
- def inherited(clazz)
16
- clazz.extend(Elements)
17
- end
18
- end
14
+ extend Elements, SelectorMethods
19
15
 
20
16
  def initialize(name, parent_page_element, type: :element, selector: {}, browser_element: nil, &block)
21
17
  @browser_element = browser_element
@@ -26,6 +22,7 @@ module PageMagic
26
22
  @parent_page_element = parent_page_element
27
23
  @type = type
28
24
  @name = name.to_s.downcase.to_sym
25
+ @element_definitions = self.class.element_definitions.dup
29
26
  expand(&block) if block
30
27
  end
31
28
 
@@ -53,7 +50,7 @@ module PageMagic
53
50
  end
54
51
 
55
52
  def method_missing(method, *args, &block)
56
- ElementContext.new(self, browser_element, self, *args).send(method, args.first, &block)
53
+ ElementContext.new(self).send(method, args.first, &block)
57
54
  rescue ElementMissingException
58
55
  begin
59
56
  return browser_element.send(method, *args, &block) if browser_element.respond_to?(method)
@@ -74,7 +71,7 @@ module PageMagic
74
71
 
75
72
  query = Query.find(type).build(query_selector, query_options)
76
73
 
77
- @browser_element = parent_browser_element.send(:find, *query).tap do |raw_element|
74
+ @browser_element = parent_browser_element.find(*query).tap do |raw_element|
78
75
  wrap_events(raw_element)
79
76
  end
80
77
  end
@@ -95,8 +92,8 @@ module PageMagic
95
92
  selector.dup.delete_if { |key, _value| key == selector.keys.first }
96
93
  end
97
94
 
98
- def element_context(*args)
99
- ElementContext.new(self, @browser_element, self, *args)
95
+ def element_context
96
+ ElementContext.new(self)
100
97
  end
101
98
 
102
99
  def wrap_events(raw_element)
@@ -1,17 +1,13 @@
1
1
  module PageMagic
2
- class ElementMissingException < Exception
3
- end
4
-
2
+ # class ElementContext - resolves which element definition to use when accessing the browser.
5
3
  class ElementContext
6
- EVENT_TYPES = [:set, :select, :select_option, :unselect_option, :click]
7
-
8
- attr_reader :caller, :page_element
4
+ attr_reader :page_element
9
5
 
10
- def initialize(page_element, caller, *_args)
6
+ def initialize(page_element)
11
7
  @page_element = page_element
12
- @caller = caller
13
8
  end
14
9
 
10
+ # acts as proxy to element defintions defined on @page_element
15
11
  def method_missing(method, *args, &block)
16
12
  return page_element.send(method, *args, &block) if page_element.methods.include?(method)
17
13
 
@@ -1,28 +1,51 @@
1
1
  require 'active_support/inflector'
2
2
  module PageMagic
3
+ # module Elements - contains methods that add element definitions to the objects it is mixed in to
3
4
  module Elements
4
5
  INVALID_METHOD_NAME_MSG = 'a method already exists with this method name'
6
+ TYPES = [:text_field, :button, :link, :checkbox, :select_list, :radios, :textarea]
5
7
 
6
- module InstanceOnlyMethods
7
- def element_definitions
8
- self.class.element_definitions
9
- end
10
- end
11
-
12
- def self.extended(clazz)
13
- clazz.class_eval do
14
- include InstanceOnlyMethods
8
+ class << self
9
+ def included(clazz)
10
+ def clazz.inherited(clazz)
11
+ clazz.element_definitions.merge!(element_definitions)
12
+ end
15
13
  end
16
- end
17
-
18
- def method_added(method)
19
- fail InvalidMethodNameException, 'method name matches element name' if element_definitions[method]
14
+ alias_method :extended, :included
20
15
  end
21
16
 
22
17
  def elements(browser_element, *args)
23
18
  element_definitions.values.collect { |definition| definition.call(browser_element, *args) }
24
19
  end
25
20
 
21
+ # Creates an element defintion.
22
+ # Element defintions contain specifications for locating them and other sub elements.
23
+ # if a block is specified then it will be executed against the element defintion.
24
+ # This method is aliased to each of the names specified in {TYPES TYPES}
25
+ # @example
26
+ # element :widget, id: 'widget' do
27
+ # link :next, text: 'next'
28
+ # end
29
+ # @overload element(name, selector, &block)
30
+ # @param [Symbol] name the name of the element.
31
+ # @param [Hash] selector a key value pair defining the method for locating this element
32
+ # @option selector [String] :text text contained within the element
33
+ # @option selector [String] :css css selector
34
+ # @option selector [String] :id the id of the element
35
+ # @option selector [String] :name the value of the name attribute belonging to the element
36
+ # @option selector [String] :label value of the label tied to the require field
37
+ # @overload element(element_class, &block)
38
+ # @param [ElementClass] element_class a custom class of element that inherits {Element}.
39
+ # the name of the element is derived from the class name. the Class name coverted to snakecase.
40
+ # The selector must be defined on the class itself.
41
+ # @overload element(name, element_class, &block)
42
+ # @param [Symbol] name the name of the element.
43
+ # @param [ElementClass] element_class a custom class of element that inherits {Element}.
44
+ # The selector must be defined on the class itself.
45
+ # @overload element(name, element_class, selector, &block)
46
+ # @param [Symbol] name the name of the element.
47
+ # @param [ElementClass] element_class a custom class of element that inherits {Element}.
48
+ # @param [Hash] selector a key value pair defining the method for locating this element. See above for details
26
49
  def element(*args, &block)
27
50
  block ||= proc {}
28
51
 
@@ -38,10 +61,14 @@ module PageMagic
38
61
  end
39
62
  end
40
63
 
41
- TYPES = [:text_field, :button, :link, :checkbox, :select_list, :radios, :textarea]
42
-
43
64
  TYPES.each { |type| alias_method type, :element }
44
65
 
66
+ def element_definitions
67
+ @element_definitions ||= {}
68
+ end
69
+
70
+ private
71
+
45
72
  def add_element_definition(name, &block)
46
73
  fail InvalidElementNameException, 'duplicate page element defined' if element_definitions[name]
47
74
 
@@ -51,12 +78,10 @@ module PageMagic
51
78
  element_definitions[name] = block
52
79
  end
53
80
 
54
- def element_definitions
55
- @element_definitions ||= {}
81
+ def method_added(method)
82
+ fail InvalidMethodNameException, 'method name matches element name' if element_definitions[method]
56
83
  end
57
84
 
58
- private
59
-
60
85
  def remove_argument(args, clazz)
61
86
  argument = args.find { |arg| arg.is_a?(clazz) }
62
87
  args.delete(argument)
@@ -1,10 +1,8 @@
1
1
  module PageMagic
2
2
  class UnsupportedSelectorException < Exception
3
3
  end
4
- class UndefinedSelectorException < Exception
5
- end
6
4
 
7
- class MissingLocatorOrSelector < Exception
5
+ class UndefinedSelectorException < Exception
8
6
  end
9
7
 
10
8
  class InvalidElementNameException < Exception
@@ -15,4 +13,13 @@ module PageMagic
15
13
 
16
14
  class UnsupportedCriteriaException < Exception
17
15
  end
16
+
17
+ class ElementMissingException < Exception
18
+ end
19
+
20
+ class InvalidURLException < Exception
21
+ end
22
+
23
+ class UnspportedBrowserException < Exception
24
+ end
18
25
  end
@@ -0,0 +1,48 @@
1
+ module PageMagic
2
+ # module InstanceMethods - provides instance level methods for page objects
3
+ module InstanceMethods
4
+ attr_reader :browser, :session, :browser_element
5
+
6
+ # Creates a new instance
7
+ # @param [Session] session session that provides gateway to the browser throw the users chosen browser
8
+ def initialize(session = Session.new(Capybara.current_session))
9
+ @browser = session.raw_session
10
+ @session = session
11
+
12
+ @browser_element = browser
13
+ end
14
+
15
+ def title
16
+ browser.title
17
+ end
18
+
19
+ def text_on_page?(string)
20
+ text.downcase.include?(string.downcase)
21
+ end
22
+
23
+ def visit
24
+ browser.visit self.class.url
25
+ self
26
+ end
27
+
28
+ def text
29
+ browser.text
30
+ end
31
+
32
+ def method_missing(method, *args)
33
+ element_context.send(method, *args)
34
+ end
35
+
36
+ def respond_to?(*args)
37
+ super || element_context.respond_to?(*args)
38
+ end
39
+
40
+ def element_definitions
41
+ self.class.element_definitions
42
+ end
43
+
44
+ def element_context
45
+ ElementContext.new(self)
46
+ end
47
+ end
48
+ end
@@ -1,30 +1,54 @@
1
1
  require 'wait'
2
2
  module PageMagic
3
- class InvalidURLException < Exception
4
- end
5
-
3
+ # class Session - coordinates access to the browser though page objects.
6
4
  class Session
7
5
  URL_MISSING_MSG = 'a path must be mapped or a url supplied'
8
6
  REGEXP_MAPPING_MSG = 'URL could not be derived because mapping is a Regexp'
9
7
 
10
8
  attr_reader :raw_session, :transitions
11
9
 
12
- def initialize(browser, url = nil)
13
- @raw_session = browser
14
- raw_session.visit(url) if url
10
+ # Create a new session instance
11
+ # @param [Object] capybara_session an instance of a capybara session
12
+ # @param [String] url url to start the session at.
13
+ def initialize(capybara_session, url = nil)
14
+ @raw_session = capybara_session
15
+ visit(url: url) if url
15
16
  @transitions = {}
16
17
  end
17
18
 
19
+ # Map paths to Page classes. The session will auto load page objects from these mapping when
20
+ # the {Session#current_path}
21
+ # is matched.
22
+ # @example
23
+ # self.define_page_mappings '/' => HomePage, %r{/messages/d+}
24
+ # @param [Hash] transitions - paths mapped to page classes
25
+ # @option transitions [String] path as literal
26
+ # @option transitions [Regexp] path as a regexp for dynamic matching.
18
27
  def define_page_mappings(transitions)
19
28
  @transitions = transitions
20
29
  end
21
30
 
31
+ # @return [Object] returns page object representing the currently loaded page on the browser. If no mapping
32
+ # is found then nil returned
22
33
  def current_page
23
34
  mapping = find_mapped_page(current_path)
24
35
  @current_page = mapping.new(self) if mapping
25
36
  @current_page
26
37
  end
27
38
 
39
+ # Direct the browser to the given page or url. {Session#current_page} will be set be an instance of the given/mapped
40
+ # page class
41
+ # @overload visit(page: page_object)
42
+ # @param [Object] page page class. The required url will be generated using the session's base url and the mapped
43
+ # path
44
+ # @overload visit(url: url)
45
+ # @param [String] url url to be visited.
46
+ # @overload visit(page: page_class, url: url)
47
+ # @param [String] url url to be visited.
48
+ # @param [Object] page the supplied page class will be instantiated to be used against the given url.
49
+ # @raise [InvalidURLException] if a page is supplied and there isn't a mapped path for it
50
+ # @raise [InvalidURLException] if neither a page or url are supplied
51
+ # @raise [InvalidURLException] if the mapped path for a page is a Regexp
28
52
  def visit(page = nil, url: nil)
29
53
  if url
30
54
  raw_session.visit(url)
@@ -38,33 +62,46 @@ module PageMagic
38
62
  self
39
63
  end
40
64
 
41
- def url(base_url, path)
42
- path = path.sub(%r{^/}, '')
43
- base_url = base_url.sub(%r{/$}, '')
44
- "#{base_url}/#{path}"
45
- end
46
-
65
+ # @return [String] path in the browser
47
66
  def current_path
48
67
  raw_session.current_path
49
68
  end
50
69
 
70
+ # @return [String] full url in the browser
51
71
  def current_url
52
72
  raw_session.current_url
53
73
  end
54
74
 
75
+ # Wait until a the supplied block returns true
76
+ # @example
77
+ # wait_until do
78
+ # (rand % 2) == 0
79
+ # end
55
80
  def wait_until(&block)
56
81
  @wait ||= Wait.new
57
82
  @wait.until(&block)
58
83
  end
59
84
 
85
+ # proxies unknown method calls to the currently loaded page object
86
+ # @return [Object] returned object from the page object method call
60
87
  def method_missing(name, *args, &block)
61
88
  current_page.send(name, *args, &block)
62
89
  end
63
90
 
91
+ # @param args see {::Object#respond_to?}
92
+ # @return [Boolean] true if self or the current page object responds to the give method name
64
93
  def respond_to?(*args)
65
94
  super || current_page.respond_to?(*args)
66
95
  end
67
96
 
97
+ private
98
+
99
+ def url(base_url, path)
100
+ path = path.sub(%r{^/}, '')
101
+ base_url = base_url.sub(%r{/$}, '')
102
+ "#{base_url}/#{path}"
103
+ end
104
+
68
105
  def find_mapped_page(path)
69
106
  mapping = transitions.keys.find do |key|
70
107
  string_matches?(path, key)
@@ -72,8 +109,6 @@ module PageMagic
72
109
  transitions[mapping]
73
110
  end
74
111
 
75
- private
76
-
77
112
  def string_matches?(string, matcher)
78
113
  if matcher.is_a?(Regexp)
79
114
  string =~ matcher
data/lib/page_magic.rb CHANGED
@@ -2,26 +2,14 @@ $LOAD_PATH.unshift("#{File.dirname(__FILE__)}")
2
2
  require 'capybara'
3
3
  require 'page_magic/exceptions'
4
4
  require 'page_magic/session'
5
+ require 'page_magic/instance_methods'
5
6
  require 'page_magic/elements'
6
7
  require 'page_magic/element_context'
7
8
  require 'page_magic/element'
8
- require 'page_magic/page_magic'
9
9
  require 'page_magic/drivers'
10
10
 
11
+ # module PageMagic - PageMagic is an api for modelling pages in a website.
11
12
  module PageMagic
12
- class UnspportedBrowserException < Exception; end
13
-
14
- module ClassMethods
15
- def url(url = nil)
16
- @url = url if url
17
- @url
18
- end
19
-
20
- def inherited(clazz)
21
- clazz.element_definitions.merge!(element_definitions)
22
- end
23
- end
24
-
25
13
  class << self
26
14
  def drivers
27
15
  @drivers ||= Drivers.new.tap(&:load)
@@ -39,7 +27,15 @@ module PageMagic
39
27
  end
40
28
 
41
29
  def included(clazz)
42
- clazz.extend(Elements, ClassMethods)
30
+ clazz.class_eval do
31
+ def self.url(url = nil)
32
+ @url = url if url
33
+ @url
34
+ end
35
+
36
+ include(InstanceMethods)
37
+ extend(Elements)
38
+ end
43
39
  end
44
40
  end
45
41
  end
@@ -1,11 +1,9 @@
1
1
  require 'page_magic/drivers/poltergeist'
2
2
  module PageMagic
3
- class Drivers
4
- describe Poltergeist do
5
- it "is capybara's poltergeist driver" do
6
- driver = described_class.build(:app, browser: :poltergeist, options: {})
7
- expect(driver).to be_a(Capybara::Poltergeist::Driver)
8
- end
3
+ describe Drivers::Poltergeist do
4
+ it "is capybara's poltergeist driver" do
5
+ driver = described_class.build(:app, browser: :poltergeist, options: {})
6
+ expect(driver).to be_a(Capybara::Poltergeist::Driver)
9
7
  end
10
8
  end
11
9
  end
@@ -1,18 +1,16 @@
1
1
  require 'page_magic/drivers/selenium'
2
2
  module PageMagic
3
- class Drivers
4
- describe Selenium do
5
- subject do
6
- described_class.build(:app, browser: :selenium, options: {})
7
- end
3
+ describe Drivers::Selenium do
4
+ subject do
5
+ described_class.build(:app, browser: :selenium, options: {})
6
+ end
8
7
 
9
- it 'is selenium' do
10
- expect(subject).to be_a(Capybara::Selenium::Driver)
11
- end
8
+ it 'is selenium' do
9
+ expect(subject).to be_a(Capybara::Selenium::Driver)
10
+ end
12
11
 
13
- it 'sets the browser option' do
14
- expect(subject.options[:browser]).to be(:selenium)
15
- end
12
+ it 'sets the browser option' do
13
+ expect(subject.options[:browser]).to be(:selenium)
16
14
  end
17
15
  end
18
16
  end
@@ -1,5 +1,4 @@
1
1
  require 'page_magic/drivers'
2
-
3
2
  module PageMagic
4
3
  describe Drivers do
5
4
  subject { described_class.new }
@@ -21,13 +21,13 @@ module PageMagic
21
21
 
22
22
  context 'neither a method or page element are defined' do
23
23
  it 'raises an error' do
24
- expect { described_class.new(page, page.browser, self).missing_thing }.to raise_error ElementMissingException
24
+ expect { described_class.new(page).missing_thing }.to raise_error ElementMissingException
25
25
  end
26
26
  end
27
27
 
28
28
  context 'method is a element defintion' do
29
29
  it 'returns the sub page element' do
30
- element = described_class.new(page, page.browser, self).a_link
30
+ element = described_class.new(page).a_link
31
31
  # TODO: - returns the capybara object. maybe we should think about wrapping this.
32
32
  expect(element.text).to eq('a link')
33
33
  end
@@ -39,7 +39,7 @@ module PageMagic
39
39
  end
40
40
  end
41
41
 
42
- described_class.new(page, page.browser, self).a_link
42
+ described_class.new(page).a_link
43
43
  end
44
44
  end
45
45
 
@@ -51,14 +51,14 @@ module PageMagic
51
51
  end
52
52
  end
53
53
 
54
- expect(described_class.new(page, :browser, self).page_method).to eq(:called)
54
+ expect(described_class.new(page).page_method).to eq(:called)
55
55
  end
56
56
  end
57
57
  end
58
58
 
59
59
  describe '#respond_to?' do
60
60
  subject do
61
- described_class.new(elements_page.new(session), session, self)
61
+ described_class.new(elements_page.new(session))
62
62
  end
63
63
  it 'checks against the names of the elements passed in' do
64
64
  expect(subject.respond_to?(:a_link)).to eq(true)
@@ -0,0 +1,63 @@
1
+ module PageMagic
2
+ describe InstanceMethods do
3
+ include_context :webapp_fixture
4
+ subject do
5
+ clazz = Class.new do
6
+ include PageMagic
7
+ url '/page1'
8
+ link(:next_page, text: 'next page')
9
+ end
10
+ clazz.new.tap(&:visit)
11
+ end
12
+
13
+ context '#respond_to?' do
14
+ it 'checks self' do
15
+ expect(subject.respond_to?(:visit)).to eq(true)
16
+ end
17
+
18
+ it 'checks the current page' do
19
+ expect(subject.respond_to?(:next_page)).to eq(true)
20
+ end
21
+ end
22
+
23
+ describe 'visit' do
24
+ it 'goes to the class define url' do
25
+ expect(subject.session.current_path).to eq('/page1')
26
+ end
27
+ end
28
+
29
+ describe 'session' do
30
+ it 'gives access to the page magic object wrapping the user session' do
31
+ expect(subject.session.raw_session).to be(Capybara.current_session)
32
+ end
33
+ end
34
+
35
+ describe 'text_on_page?' do
36
+ it 'returns true if the text is present' do
37
+ expect(subject.text_on_page?('next page')).to eq(true)
38
+ end
39
+
40
+ it 'returns false if the text is not present' do
41
+ expect(subject.text_on_page?('not on page')).to eq(false)
42
+ end
43
+ end
44
+
45
+ describe 'title' do
46
+ it 'returns the title' do
47
+ expect(subject.title).to eq('page1')
48
+ end
49
+ end
50
+
51
+ describe 'text' do
52
+ it 'returns the text on the page' do
53
+ expect(subject.text).to eq('next page')
54
+ end
55
+ end
56
+
57
+ describe 'method_missing' do
58
+ it 'gives access to the elements defined on your page classes' do
59
+ expect(subject.next_page.tag_name).to eq('a')
60
+ end
61
+ end
62
+ end
63
+ end
@@ -61,18 +61,18 @@ module PageMagic
61
61
 
62
62
  context 'mapping is string' do
63
63
  it 'returns the page class' do
64
- expect(subject.find_mapped_page('/page')).to be(:mapped_page_using_string)
64
+ expect(subject.instance_eval { find_mapped_page('/page') }).to be(:mapped_page_using_string)
65
65
  end
66
66
  end
67
67
  context 'mapping is regex' do
68
68
  it 'returns the page class' do
69
- expect(subject.find_mapped_page('/page2')).to be(:mapped_page_using_regex)
69
+ expect(subject.instance_eval { find_mapped_page('/page2') }).to be(:mapped_page_using_regex)
70
70
  end
71
71
  end
72
72
 
73
73
  context 'mapping is not found' do
74
74
  it 'returns nil' do
75
- expect(subject.find_mapped_page('/fake_page')).to be(nil)
75
+ expect(subject.instance_eval { find_mapped_page('/fake_page') }).to be(nil)
76
76
  end
77
77
  end
78
78
  end
@@ -129,13 +129,13 @@ module PageMagic
129
129
 
130
130
  context 'path has / at the beginning' do
131
131
  it 'produces compound url' do
132
- expect(subject.url(base_url, path)).to eq(expected_url)
132
+ expect(subject.send(:url, base_url, path)).to eq(expected_url)
133
133
  end
134
134
  end
135
135
 
136
136
  context 'path does not have / at the beginning' do
137
137
  it 'produces compound url' do
138
- expect(subject.url(base_url, "/#{path}")).to eq(expected_url)
138
+ expect(subject.send(:url, base_url, "/#{path}")).to eq(expected_url)
139
139
  end
140
140
  end
141
141
  end
@@ -143,13 +143,13 @@ module PageMagic
143
143
  context 'current_url does not have a / on the end' do
144
144
  context 'path has / at the beginning' do
145
145
  it 'produces compound url' do
146
- expect(subject.url(base_url, "/#{path}")).to eq(expected_url)
146
+ expect(subject.send(:url, base_url, "/#{path}")).to eq(expected_url)
147
147
  end
148
148
  end
149
149
 
150
150
  context 'path does not have / at the beginning' do
151
151
  it 'produces compound url' do
152
- expect(subject.url(base_url, path)).to eq(expected_url)
152
+ expect(subject.send(:url, base_url, path)).to eq(expected_url)
153
153
  end
154
154
  end
155
155
  end
data/spec/spec_helper.rb CHANGED
@@ -1,7 +1,8 @@
1
1
  Bundler.require
2
- $LOAD_PATH.unshift(__dir__, "#{__dir__}/../lib")
3
-
2
+ $LOAD_PATH.unshift("#{File.dirname(__FILE__)}/lib")
3
+ require 'pry-byebug'
4
4
  require 'support/shared_contexts'
5
+
5
6
  require 'simplecov' if ENV['coverage']
6
7
 
7
8
  require 'page_magic'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: page_magic
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.0.alpha17
4
+ version: 1.0.0.alpha18
5
5
  platform: ruby
6
6
  authors:
7
7
  - Leon Davis
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-27 00:00:00.000000000 Z
11
+ date: 2015-11-02 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: capybara
@@ -80,6 +80,62 @@ dependencies:
80
80
  - - ">="
81
81
  - !ruby/object:Gem::Version
82
82
  version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: pry-byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: yard
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: redcarpet
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: github-markup
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - ">="
130
+ - !ruby/object:Gem::Version
131
+ version: '0'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - ">="
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
83
139
  description: Framework for modeling and interacting with webpages which wraps capybara
84
140
  email: info@lad-tech.com
85
141
  executables: []
@@ -87,16 +143,20 @@ extensions: []
87
143
  extra_rdoc_files:
88
144
  - README.md
89
145
  files:
146
+ - ".codeclimate.yml"
147
+ - ".pullreview.yml"
90
148
  - ".rspec"
91
149
  - ".rubocop.yml"
92
150
  - ".ruby-gemset"
93
151
  - ".ruby-version"
94
152
  - ".simplecov"
153
+ - ".yardopts"
95
154
  - Gemfile
96
155
  - Gemfile.lock
97
156
  - README.md
98
157
  - Rakefile
99
158
  - VERSION
159
+ - circle.yml
100
160
  - lib/page_magic.rb
101
161
  - lib/page_magic/driver.rb
102
162
  - lib/page_magic/drivers.rb
@@ -111,7 +171,7 @@ files:
111
171
  - lib/page_magic/element_context.rb
112
172
  - lib/page_magic/elements.rb
113
173
  - lib/page_magic/exceptions.rb
114
- - lib/page_magic/page_magic.rb
174
+ - lib/page_magic/instance_methods.rb
115
175
  - lib/page_magic/session.rb
116
176
  - page_magic.gemspec
117
177
  - spec/element_spec.rb
@@ -125,7 +185,7 @@ files:
125
185
  - spec/page_magic/element/selector_spec.rb
126
186
  - spec/page_magic/element_context_spec.rb
127
187
  - spec/page_magic/elements_spec.rb
128
- - spec/page_magic/page_magic_spec.rb
188
+ - spec/page_magic/instance_methods_spec.rb
129
189
  - spec/page_magic/session_spec.rb
130
190
  - spec/page_magic_spec.rb
131
191
  - spec/spec_helper.rb
@@ -1,40 +0,0 @@
1
- module PageMagic
2
- attr_reader :browser, :session, :browser_element
3
-
4
- def initialize(session = Session.new(Capybara.current_session), &block)
5
- @browser = session.raw_session
6
- @session = session
7
-
8
- @browser_element = browser
9
- block.call browser if block
10
- end
11
-
12
- def title
13
- browser.title
14
- end
15
-
16
- def text_on_page?(string)
17
- text.downcase.include?(string.downcase)
18
- end
19
-
20
- def visit
21
- browser.visit self.class.url
22
- self
23
- end
24
-
25
- def text
26
- browser.text
27
- end
28
-
29
- def method_missing(method, *args)
30
- element_context.send(method, *args)
31
- end
32
-
33
- def respond_to?(*args)
34
- super || element_context.respond_to?(*args)
35
- end
36
-
37
- def element_context
38
- ElementContext.new(self, self)
39
- end
40
- end
@@ -1,62 +0,0 @@
1
- describe PageMagic do
2
- include_context :webapp_fixture
3
- subject do
4
- described_class = described_class()
5
- page_class = Class.new do
6
- include described_class
7
- url '/page1'
8
- link(:next_page, text: 'next page')
9
- end
10
- page_class.new.tap(&:visit)
11
- end
12
-
13
- context '#respond_to?' do
14
- it 'checks self' do
15
- expect(subject.respond_to?(:visit)).to eq(true)
16
- end
17
-
18
- it 'checks the current page' do
19
- expect(subject.respond_to?(:next_page)).to eq(true)
20
- end
21
- end
22
-
23
- describe 'visit' do
24
- it 'goes to the class define url' do
25
- expect(subject.session.current_path).to eq('/page1')
26
- end
27
- end
28
-
29
- describe 'session' do
30
- it 'gives access to the page magic object wrapping the user session' do
31
- expect(subject.session.raw_session).to be(Capybara.current_session)
32
- end
33
- end
34
-
35
- describe 'text_on_page?' do
36
- it 'returns true if the text is present' do
37
- expect(subject.text_on_page?('next page')).to eq(true)
38
- end
39
-
40
- it 'returns false if the text is not present' do
41
- expect(subject.text_on_page?('not on page')). to eq(false)
42
- end
43
- end
44
-
45
- describe 'title' do
46
- it 'returns the title' do
47
- expect(subject.title).to eq('page1')
48
- end
49
- end
50
-
51
- describe 'text' do
52
- it 'returns the text on the page' do
53
- expect(subject.text).to eq('next page')
54
- end
55
- end
56
-
57
- describe 'method_missing' do
58
- it 'gives access to the elements defined on your page classes' do
59
- expect(subject.next_page.tag_name).to eq('a')
60
- end
61
- end
62
- end