page_magic 1.2.9 → 2.0.0.alpha1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (66) hide show
  1. checksums.yaml +5 -5
  2. data/.rubocop.yml +3 -3
  3. data/.simplecov +4 -2
  4. data/.zsh_config +6 -0
  5. data/Dockerfile +10 -0
  6. data/Gemfile +10 -11
  7. data/Gemfile.lock +120 -133
  8. data/Makefile +13 -0
  9. data/Rakefile +3 -1
  10. data/VERSION +1 -1
  11. data/lib/page_magic.rb +5 -3
  12. data/lib/page_magic/class_methods.rb +4 -1
  13. data/lib/page_magic/driver.rb +3 -0
  14. data/lib/page_magic/drivers.rb +3 -1
  15. data/lib/page_magic/drivers/poltergeist.rb +2 -0
  16. data/lib/page_magic/drivers/rack_test.rb +3 -1
  17. data/lib/page_magic/drivers/selenium.rb +4 -2
  18. data/lib/page_magic/element.rb +8 -5
  19. data/lib/page_magic/element/locators.rb +4 -1
  20. data/lib/page_magic/element/query.rb +11 -2
  21. data/lib/page_magic/element/query_builder.rb +15 -2
  22. data/lib/page_magic/element/selector.rb +3 -0
  23. data/lib/page_magic/element/selector_methods.rb +3 -0
  24. data/lib/page_magic/element_context.rb +2 -0
  25. data/lib/page_magic/element_definition_builder.rb +4 -1
  26. data/lib/page_magic/elements.rb +42 -6
  27. data/lib/page_magic/exceptions.rb +3 -1
  28. data/lib/page_magic/instance_methods.rb +2 -0
  29. data/lib/page_magic/matcher.rb +10 -1
  30. data/lib/page_magic/session.rb +7 -5
  31. data/lib/page_magic/session_methods.rb +2 -0
  32. data/lib/page_magic/utils/string.rb +2 -0
  33. data/lib/page_magic/wait_methods.rb +3 -0
  34. data/lib/page_magic/watcher.rb +2 -0
  35. data/lib/page_magic/watchers.rb +4 -1
  36. data/spec/element_spec.rb +2 -0
  37. data/spec/page_magic/class_methods_spec.rb +2 -0
  38. data/spec/page_magic/driver_spec.rb +2 -0
  39. data/spec/page_magic/drivers/poltergeist_spec.rb +2 -0
  40. data/spec/page_magic/drivers/rack_test_spec.rb +2 -0
  41. data/spec/page_magic/drivers/selenium_spec.rb +2 -0
  42. data/spec/page_magic/drivers_spec.rb +2 -0
  43. data/spec/page_magic/element/locators_spec.rb +2 -0
  44. data/spec/page_magic/element/query_builder_spec.rb +7 -5
  45. data/spec/page_magic/element/query_spec.rb +6 -4
  46. data/spec/page_magic/element/selector_spec.rb +7 -5
  47. data/spec/page_magic/element_context_spec.rb +3 -1
  48. data/spec/page_magic/element_definition_builder_spec.rb +2 -0
  49. data/spec/page_magic/elements_spec.rb +9 -0
  50. data/spec/page_magic/instance_methods_spec.rb +2 -0
  51. data/spec/page_magic/matcher_spec.rb +3 -1
  52. data/spec/page_magic/session_methods_spec.rb +2 -0
  53. data/spec/page_magic/session_spec.rb +1 -1
  54. data/spec/page_magic/utils/string_spec.rb +2 -0
  55. data/spec/page_magic/wait_methods_spec.rb +5 -3
  56. data/spec/page_magic/watchers_spec.rb +2 -0
  57. data/spec/page_magic_spec.rb +5 -7
  58. data/spec/spec_helper.rb +3 -0
  59. data/spec/support/shared_contexts.rb +3 -1
  60. data/spec/support/shared_contexts/files_context.rb +2 -0
  61. data/spec/support/shared_contexts/nested_elements_html_context.rb +2 -0
  62. data/spec/support/shared_contexts/rack_application_context.rb +2 -0
  63. data/spec/support/shared_contexts/webapp_fixture_context.rb +2 -0
  64. data/spec/support/shared_examples.rb +2 -0
  65. data/spec/watcher_spec.rb +3 -0
  66. metadata +45 -29
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  # module ClassMethods - contains class level methods for PageObjects
3
5
  module ClassMethods
@@ -15,6 +17,7 @@ module PageMagic
15
17
  # if one has not been set on the page object class it will return a default block that does nothing
16
18
  def on_load(&block)
17
19
  return @on_load || DEFAULT_ON_LOAD unless block
20
+
18
21
  @on_load = block
19
22
  end
20
23
 
@@ -34,7 +37,7 @@ module PageMagic
34
37
  session_options = { browser: browser, options: options, url: url }
35
38
  session_options[:application] = application if application
36
39
 
37
- PageMagic.session(session_options).tap do |session|
40
+ PageMagic.session(**session_options).tap do |session|
38
41
  session.visit(self, url: url)
39
42
  end
40
43
  end
@@ -1,7 +1,10 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  # class Driver - instances are factories for drivers used by PageMagic
3
5
  class Driver
4
6
  attr_reader :supported_browsers, :handler
7
+
5
8
  # Creates a driver definition
6
9
  # @example
7
10
  # Driver.new do |rack_application, options|
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'page_magic/driver'
2
4
  require 'page_magic/utils/string'
3
5
  module PageMagic
@@ -17,7 +19,7 @@ module PageMagic
17
19
  # Loads drivers defined in files at the given path
18
20
  # @param [String] path where the drivers are located
19
21
  def load(path = "#{__dir__}/drivers")
20
- Dir["#{path}/*.rb"].each do |driver_file|
22
+ Dir["#{path}/*.rb"].sort.each do |driver_file|
21
23
  require driver_file
22
24
  driver_name = File.basename(driver_file)[/(.*)\.rb$/, 1]
23
25
  register self.class.const_get(Utils::String.classify(driver_name))
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  PageMagic::Drivers::Poltergeist = PageMagic::Driver.new(:poltergeist) do |app, options|
2
4
  require 'capybara/poltergeist'
3
5
  Capybara::Poltergeist::Driver.new(app, options)
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  PageMagic::Drivers::RackTest = PageMagic::Driver.new(:rack_test) do |app, options|
2
- Capybara::RackTest::Driver.new(app, options)
4
+ Capybara::RackTest::Driver.new(app, **options)
3
5
  end
@@ -1,4 +1,6 @@
1
+ # frozen_string_literal: true
2
+
1
3
  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))
4
+ require 'watir'
5
+ Capybara::Selenium::Driver.new(app, **options.dup.merge(browser: browser))
4
6
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
  require 'page_magic/element/selector_methods'
3
5
  require 'page_magic/element/locators'
@@ -8,7 +10,7 @@ module PageMagic
8
10
  class Element
9
11
  EVENT_TYPES = %i[set select select_option unselect_option click].freeze
10
12
  DEFAULT_HOOK = proc {}.freeze
11
- EVENT_NOT_SUPPORTED_MSG = '%s event not supported'.freeze
13
+ EVENT_NOT_SUPPORTED_MSG = '%s event not supported'
12
14
 
13
15
  include Locators
14
16
  include WaitMethods
@@ -44,6 +46,7 @@ module PageMagic
44
46
  # @return [Element]
45
47
  def parent_element(page_element = nil)
46
48
  return @parent_page_element unless page_element
49
+
47
50
  @parent_page_element = page_element
48
51
  end
49
52
 
@@ -84,9 +87,7 @@ module PageMagic
84
87
  # @raise [NotSupportedException] if the wrapped Capybara element does not support the method
85
88
  EVENT_TYPES.each do |method|
86
89
  define_method method do |*args|
87
- unless browser_element.respond_to?(method)
88
- raise NotSupportedException, EVENT_NOT_SUPPORTED_MSG % method
89
- end
90
+ raise NotSupportedException, EVENT_NOT_SUPPORTED_MSG % method unless browser_element.respond_to?(method)
90
91
 
91
92
  browser_element.send(method, *args)
92
93
  end
@@ -97,7 +98,8 @@ module PageMagic
97
98
  rescue ElementMissingException
98
99
  begin
99
100
  return browser_element.send(method, *args, &block) if browser_element.respond_to?(method)
100
- return parent_element.send(method, *args, &block)
101
+
102
+ parent_element.send(method, *args, &block)
101
103
  rescue NoMethodError
102
104
  super
103
105
  end
@@ -137,6 +139,7 @@ module PageMagic
137
139
  def wrap_events(raw_element)
138
140
  EVENT_TYPES.each do |action_method|
139
141
  next unless raw_element.respond_to?(action_method)
142
+
140
143
  apply_hooks(raw_element: raw_element,
141
144
  capybara_method: action_method,
142
145
  before_events: before_events,
@@ -1,10 +1,12 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  # for the benefit of pull review :@
3
5
  class Element
4
6
  # contains method for finding element definitions
5
7
  module Locators
6
8
  # message used when raising {ElementMissingException} from methods within this module
7
- ELEMENT_NOT_DEFINED_MSG = 'Element not defined: %s'.freeze
9
+ ELEMENT_NOT_DEFINED_MSG = 'Element not defined: %s'
8
10
 
9
11
  # find an element definition based on its name
10
12
  # @param [Symbol] name name of the element
@@ -13,6 +15,7 @@ module PageMagic
13
15
  def element_by_name(name, *args)
14
16
  defintion = element_definitions[name]
15
17
  raise ElementMissingException, (ELEMENT_NOT_DEFINED_MSG % name) unless defintion
18
+
16
19
  defintion.call(self, *args)
17
20
  end
18
21
 
@@ -1,9 +1,11 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  class Element
3
5
  # class Query - executes query on capybara driver
4
6
  class Query
5
7
  # Message template for execptions raised as a result of calling method_missing
6
- ELEMENT_NOT_FOUND_MSG = 'Unable to find %s'.freeze
8
+ ELEMENT_NOT_FOUND_MSG = 'Unable to find %s'
7
9
 
8
10
  attr_reader :args, :multiple_results
9
11
 
@@ -20,7 +22,14 @@ module PageMagic
20
22
  raise Capybara::ElementNotFound if result.empty?
21
23
  end
22
24
  else
23
- capybara_element.find(*args)
25
+ # TODO - make sure there is a test around this.
26
+ if args.last.is_a?(Hash)
27
+ capybara_element.find(*args[0...-1], **args.last)
28
+ else
29
+ capybara_element.find(*args)
30
+ end
31
+
32
+
24
33
  end
25
34
  rescue Capybara::Ambiguous => e
26
35
  raise AmbiguousQueryException, e.message
@@ -1,4 +1,5 @@
1
- require 'capybara/query'
1
+ # frozen_string_literal: true
2
+
2
3
  require 'page_magic/element/query'
3
4
  module PageMagic
4
5
  class Element
@@ -14,6 +15,7 @@ module PageMagic
14
15
  def find(type)
15
16
  query = constants.find { |constant| constant.to_s.casecmp(type.to_s).zero? }
16
17
  return ELEMENT unless query
18
+
17
19
  const_get(query)
18
20
  end
19
21
  end
@@ -40,8 +42,19 @@ module PageMagic
40
42
  end
41
43
 
42
44
  ELEMENT = QueryBuilder.new
43
- TEXT_FIELD = CHECKBOX = SELECT_LIST = RADIO = TEXTAREA = QueryBuilder.new(:field)
45
+ TEXT_FIELD =
46
+ CHECKBOX =
47
+ SELECT_LIST =
48
+ RADIO =
49
+ TEXTAREA =
50
+ FIELD =
51
+ FILE_FIELD =
52
+ FILLABLE_FIELD =
53
+ RADIO_BUTTON =
54
+ SELECT = QueryBuilder.new(:field)
55
+
44
56
  LINK = QueryBuilder.new(:link)
57
+ LABEL = QueryBuilder.new(:label)
45
58
  BUTTON = QueryBuilder.new(:button)
46
59
  end
47
60
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  class Element
3
5
  # class Selector - models the selection criteria understood by Capybara
@@ -10,6 +12,7 @@ module PageMagic
10
12
  def find(name)
11
13
  selector = constants.find { |constant| constant.to_s.casecmp(name.to_s).zero? }
12
14
  raise UnsupportedCriteriaException unless selector
15
+
13
16
  const_get(selector)
14
17
  end
15
18
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  class Element
3
5
  # module SelectorMethods - adds method for getting and setting an element selector
@@ -6,6 +8,7 @@ module PageMagic
6
8
  # @param [Hash] selector method for locating the browser element. E.g. text: 'the text'
7
9
  def selector(selector = nil)
8
10
  return @selector unless selector
11
+
9
12
  @selector = selector
10
13
  end
11
14
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  # class ElementContext - resolves which element definition to use when accessing the browser.
3
5
  class ElementContext
@@ -1,7 +1,9 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  # Builder for creating ElementDefinitions
3
5
  class ElementDefinitionBuilder
4
- INVALID_SELECTOR_MSG = 'Pass a locator/define one on the class'.freeze
6
+ INVALID_SELECTOR_MSG = 'Pass a locator/define one on the class'
5
7
  attr_reader :definition_class, :options, :selector, :type, :element, :query_builder
6
8
 
7
9
  def initialize(definition_class:, selector:, type:, options: {}, element: nil)
@@ -34,6 +36,7 @@ module PageMagic
34
36
 
35
37
  def ==(other)
36
38
  return false unless other.is_a?(ElementDefinitionBuilder)
39
+
37
40
  this = [options, selector, type, element, definition_class]
38
41
  this == [other.options, other.selector, other.type, other.element, other.definition_class]
39
42
  end
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/inflector'
2
4
  require 'page_magic/element_definition_builder'
3
5
  module PageMagic
@@ -12,11 +14,44 @@ module PageMagic
12
14
  end
13
15
  end
14
16
 
15
- INVALID_METHOD_NAME_MSG = 'a method already exists with this method name'.freeze
16
-
17
- TYPES = %i[text_field button link checkbox select_list radio textarea].collect do |type|
18
- [type, :"#{type}s"]
19
- end.flatten.freeze
17
+ INVALID_METHOD_NAME_MSG = 'a method already exists with this method name'
18
+
19
+ # css
20
+ # datalist_input
21
+ # datalist_option
22
+ # field
23
+ # fieldset
24
+ # file_field
25
+ # fillable_field
26
+ # frame
27
+ # link_or_button
28
+ # option
29
+ # radio_button
30
+ # select
31
+ # table
32
+ # table_row
33
+ # xpath
34
+ #
35
+
36
+ TYPES = %i[field
37
+ fieldset
38
+ file_field
39
+ fillable_field
40
+ frame
41
+ link_or_button
42
+ option
43
+ radio_button
44
+ select
45
+ table
46
+ table_row
47
+ text_field
48
+ button
49
+ link
50
+ checkbox
51
+ select_list
52
+ radio
53
+ textarea
54
+ label].collect{ |type| [type, :"#{type}s"]}.flatten.freeze
20
55
 
21
56
  class << self
22
57
  def extended(clazz)
@@ -63,7 +98,8 @@ module PageMagic
63
98
  parent_element(parent_element)
64
99
  class_exec(*e_args, &block)
65
100
  end
66
- ElementDefinitionBuilder.new(options.merge(definition_class: definition_class))
101
+
102
+ ElementDefinitionBuilder.new(**options.merge(definition_class: definition_class))
67
103
  end
68
104
  end
69
105
 
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  class ElementMissingException < RuntimeError
3
5
  end
@@ -20,7 +22,7 @@ module PageMagic
20
22
  class TimeoutException < RuntimeError
21
23
  end
22
24
 
23
- class UnspportedBrowserException < RuntimeError
25
+ class UnsupportedBrowserException < RuntimeError
24
26
  end
25
27
 
26
28
  class UnsupportedCriteriaException < RuntimeError
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  module PageMagic
2
4
  # module InstanceMethods - provides instance level methods for page objects
3
5
  module InstanceMethods
@@ -1,3 +1,5 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'active_support/core_ext/object/to_query'
2
4
  module PageMagic
3
5
  # models mapping used to relate pages to uris
@@ -10,6 +12,7 @@ module PageMagic
10
12
  # @raise [MatcherInvalidException] if at least one component is not specified
11
13
  def initialize(path = nil, parameters: nil, fragment: nil)
12
14
  raise MatcherInvalidException unless path || parameters || fragment
15
+
13
16
  @path = path
14
17
  @parameters = parameters
15
18
  @fragment = fragment
@@ -22,7 +25,7 @@ module PageMagic
22
25
 
23
26
  # @return [String] uri represented by this mapping
24
27
  def compute_uri
25
- path.to_s.tap do |uri|
28
+ path.to_s.dup.tap do |uri|
26
29
  uri << "?#{parameters.to_query}" if parameters
27
30
  uri << "##{fragment}" if fragment
28
31
  end
@@ -55,6 +58,7 @@ module PageMagic
55
58
  # @return [Boolean]
56
59
  def ==(other)
57
60
  return false unless other.is_a?(Matcher)
61
+
58
62
  path == other.path && parameters == other.parameters && fragment == other.fragment
59
63
  end
60
64
 
@@ -64,11 +68,13 @@ module PageMagic
64
68
 
65
69
  def compare(this, other)
66
70
  return presence_comparison(this, other) unless this && other
71
+
67
72
  fuzzy_comparison(this, other)
68
73
  end
69
74
 
70
75
  def compatible?(string, comparitor)
71
76
  return true if comparitor.nil?
77
+
72
78
  if fuzzy?(comparitor)
73
79
  string =~ comparitor ? true : false
74
80
  else
@@ -82,6 +88,7 @@ module PageMagic
82
88
 
83
89
  def fuzzy?(component)
84
90
  return false unless component
91
+
85
92
  if component.is_a?(Hash)
86
93
  component.values.any? { |o| fuzzy?(o) }
87
94
  else
@@ -108,11 +115,13 @@ module PageMagic
108
115
  def presence_comparison(this, other)
109
116
  return 0 if this.nil? && other.nil?
110
117
  return 1 if this.nil? && other
118
+
111
119
  -1
112
120
  end
113
121
 
114
122
  def query_string_valid?(string)
115
123
  return true unless parameters
124
+
116
125
  parameters.none? do |key, value|
117
126
  !compatible?(parameters_hash(string)[key.downcase.to_s], value)
118
127
  end
@@ -1,12 +1,14 @@
1
+ # frozen_string_literal: true
2
+
1
3
  require 'forwardable'
2
4
  require 'page_magic/matcher'
3
5
  module PageMagic
4
6
  # class Session - coordinates access to the browser though page objects.
5
7
  class Session
6
- URL_MISSING_MSG = 'a path must be mapped or a url supplied'.freeze
7
- REGEXP_MAPPING_MSG = 'URL could not be derived because mapping contains Regexps'.freeze
8
- INVALID_MAPPING_MSG = 'mapping must be a string or regexp'.freeze
9
- UNSUPPORTED_OPERATION_MSG = 'execute_script not supported by driver'.freeze
8
+ URL_MISSING_MSG = 'a path must be mapped or a url supplied'
9
+ REGEXP_MAPPING_MSG = 'URL could not be derived because mapping contains Regexps'
10
+ INVALID_MAPPING_MSG = 'mapping must be a string or regexp'
11
+ UNSUPPORTED_OPERATION_MSG = 'execute_script not supported by driver'
10
12
 
11
13
  extend Forwardable
12
14
 
@@ -18,7 +20,6 @@ module PageMagic
18
20
  def initialize(capybara_session, base_url = nil)
19
21
  @raw_session = capybara_session
20
22
  @base_url = base_url
21
- visit(url: base_url) if base_url
22
23
  @transitions = {}
23
24
  end
24
25
 
@@ -94,6 +95,7 @@ module PageMagic
94
95
  target_url = url || begin
95
96
  if (mapping = transitions.key(page))
96
97
  raise InvalidURLException, REGEXP_MAPPING_MSG unless mapping.can_compute_uri?
98
+
97
99
  url(base_url, mapping.compute_uri)
98
100
  end
99
101
  end