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.
- checksums.yaml +5 -5
- data/.rubocop.yml +3 -3
- data/.simplecov +4 -2
- data/.zsh_config +6 -0
- data/Dockerfile +10 -0
- data/Gemfile +10 -11
- data/Gemfile.lock +120 -133
- data/Makefile +13 -0
- data/Rakefile +3 -1
- data/VERSION +1 -1
- data/lib/page_magic.rb +5 -3
- data/lib/page_magic/class_methods.rb +4 -1
- data/lib/page_magic/driver.rb +3 -0
- data/lib/page_magic/drivers.rb +3 -1
- data/lib/page_magic/drivers/poltergeist.rb +2 -0
- data/lib/page_magic/drivers/rack_test.rb +3 -1
- data/lib/page_magic/drivers/selenium.rb +4 -2
- data/lib/page_magic/element.rb +8 -5
- data/lib/page_magic/element/locators.rb +4 -1
- data/lib/page_magic/element/query.rb +11 -2
- data/lib/page_magic/element/query_builder.rb +15 -2
- data/lib/page_magic/element/selector.rb +3 -0
- data/lib/page_magic/element/selector_methods.rb +3 -0
- data/lib/page_magic/element_context.rb +2 -0
- data/lib/page_magic/element_definition_builder.rb +4 -1
- data/lib/page_magic/elements.rb +42 -6
- data/lib/page_magic/exceptions.rb +3 -1
- data/lib/page_magic/instance_methods.rb +2 -0
- data/lib/page_magic/matcher.rb +10 -1
- data/lib/page_magic/session.rb +7 -5
- data/lib/page_magic/session_methods.rb +2 -0
- data/lib/page_magic/utils/string.rb +2 -0
- data/lib/page_magic/wait_methods.rb +3 -0
- data/lib/page_magic/watcher.rb +2 -0
- data/lib/page_magic/watchers.rb +4 -1
- data/spec/element_spec.rb +2 -0
- data/spec/page_magic/class_methods_spec.rb +2 -0
- data/spec/page_magic/driver_spec.rb +2 -0
- data/spec/page_magic/drivers/poltergeist_spec.rb +2 -0
- data/spec/page_magic/drivers/rack_test_spec.rb +2 -0
- data/spec/page_magic/drivers/selenium_spec.rb +2 -0
- data/spec/page_magic/drivers_spec.rb +2 -0
- data/spec/page_magic/element/locators_spec.rb +2 -0
- data/spec/page_magic/element/query_builder_spec.rb +7 -5
- data/spec/page_magic/element/query_spec.rb +6 -4
- data/spec/page_magic/element/selector_spec.rb +7 -5
- data/spec/page_magic/element_context_spec.rb +3 -1
- data/spec/page_magic/element_definition_builder_spec.rb +2 -0
- data/spec/page_magic/elements_spec.rb +9 -0
- data/spec/page_magic/instance_methods_spec.rb +2 -0
- data/spec/page_magic/matcher_spec.rb +3 -1
- data/spec/page_magic/session_methods_spec.rb +2 -0
- data/spec/page_magic/session_spec.rb +1 -1
- data/spec/page_magic/utils/string_spec.rb +2 -0
- data/spec/page_magic/wait_methods_spec.rb +5 -3
- data/spec/page_magic/watchers_spec.rb +2 -0
- data/spec/page_magic_spec.rb +5 -7
- data/spec/spec_helper.rb +3 -0
- data/spec/support/shared_contexts.rb +3 -1
- data/spec/support/shared_contexts/files_context.rb +2 -0
- data/spec/support/shared_contexts/nested_elements_html_context.rb +2 -0
- data/spec/support/shared_contexts/rack_application_context.rb +2 -0
- data/spec/support/shared_contexts/webapp_fixture_context.rb +2 -0
- data/spec/support/shared_examples.rb +2 -0
- data/spec/watcher_spec.rb +3 -0
- 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
|
data/lib/page_magic/driver.rb
CHANGED
@@ -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|
|
data/lib/page_magic/drivers.rb
CHANGED
@@ -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,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
|
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
|
data/lib/page_magic/element.rb
CHANGED
@@ -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'
|
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
|
-
|
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'
|
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'
|
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
|
-
|
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
|
-
|
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 =
|
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,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'
|
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
|
data/lib/page_magic/elements.rb
CHANGED
@@ -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'
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
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
|
-
|
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
|
25
|
+
class UnsupportedBrowserException < RuntimeError
|
24
26
|
end
|
25
27
|
|
26
28
|
class UnsupportedCriteriaException < RuntimeError
|
data/lib/page_magic/matcher.rb
CHANGED
@@ -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
|
data/lib/page_magic/session.rb
CHANGED
@@ -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'
|
7
|
-
REGEXP_MAPPING_MSG = 'URL could not be derived because mapping contains Regexps'
|
8
|
-
INVALID_MAPPING_MSG = 'mapping must be a string or regexp'
|
9
|
-
UNSUPPORTED_OPERATION_MSG = 'execute_script not supported by driver'
|
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
|