page_magic 1.2.9 → 2.0.0.alpha1

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.
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