page_magic 1.0.0.alpha17 → 1.0.0.alpha18

Sign up to get free protection for your applications and to get access to all the features.
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