howitzer 2.0.3 → 2.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +5 -5
- data/CHANGELOG.md +69 -11
- data/LICENSE +1 -1
- data/README.md +21 -17
- data/bin/howitzer +7 -6
- data/generators/base_generator.rb +31 -17
- data/generators/config/config_generator.rb +11 -3
- data/generators/config/templates/boot.rb +3 -3
- data/generators/config/templates/capybara.rb +6 -131
- data/generators/config/templates/default.yml +34 -13
- data/generators/config/templates/drivers/appium.rb +25 -0
- data/generators/config/templates/drivers/browserstack.rb +23 -0
- data/generators/config/templates/drivers/crossbrowsertesting.rb +29 -0
- data/generators/config/templates/drivers/headless_chrome.rb +15 -0
- data/generators/config/templates/drivers/headless_firefox.rb +23 -0
- data/generators/config/templates/drivers/sauce.rb +25 -0
- data/generators/config/templates/drivers/selenium.rb +24 -0
- data/generators/config/templates/drivers/selenium_grid.rb +31 -0
- data/generators/config/templates/drivers/testingbot.rb +24 -0
- data/generators/cucumber/cucumber_generator.rb +2 -2
- data/generators/cucumber/templates/common_steps.rb +3 -3
- data/generators/cucumber/templates/cucumber.rake +5 -13
- data/generators/cucumber/templates/cuke_sniffer.rake +2 -2
- data/generators/cucumber/templates/env.rb +9 -1
- data/generators/cucumber/templates/hooks.rb +8 -2
- data/generators/cucumber/templates/transformers.rb +11 -25
- data/generators/emails/emails_generator.rb +2 -2
- data/generators/emails/templates/example_email.rb +1 -1
- data/generators/prerequisites/prerequisites_generator.rb +3 -3
- data/generators/prerequisites/templates/base.rb +1 -1
- data/generators/prerequisites/templates/{factory_girl.rb → factory_bot.rb} +7 -6
- data/generators/prerequisites/templates/users.rb +1 -1
- data/generators/root/root_generator.rb +3 -3
- data/generators/root/templates/Gemfile.erb +16 -22
- data/generators/rspec/rspec_generator.rb +2 -2
- data/generators/rspec/templates/rspec.rake +8 -8
- data/generators/rspec/templates/spec_helper.rb +6 -5
- data/generators/tasks/tasks_generator.rb +2 -2
- data/generators/turnip/templates/spec_helper.rb +6 -5
- data/generators/turnip/turnip_generator.rb +2 -2
- data/generators/web/templates/example_page.rb +1 -1
- data/generators/web/web_generator.rb +2 -2
- data/lib/howitzer/cache.rb +20 -19
- data/lib/howitzer/capybara_helpers.rb +58 -13
- data/lib/howitzer/email.rb +3 -2
- data/lib/howitzer/exceptions.rb +21 -20
- data/lib/howitzer/gmail_api/client.rb +31 -0
- data/lib/howitzer/gmail_api.rb +7 -0
- data/lib/howitzer/log.rb +6 -6
- data/lib/howitzer/mail_adapters/gmail.rb +96 -0
- data/lib/howitzer/mail_adapters/mailgun.rb +4 -2
- data/lib/howitzer/mail_adapters/mailtrap.rb +108 -0
- data/lib/howitzer/mailgun_api/client.rb +3 -2
- data/lib/howitzer/mailgun_api/connector.rb +1 -0
- data/lib/howitzer/mailgun_api/response.rb +1 -2
- data/lib/howitzer/mailtrap_api/client.rb +52 -0
- data/lib/howitzer/mailtrap_api.rb +7 -0
- data/lib/howitzer/meta/actions.rb +35 -0
- data/lib/howitzer/meta/element.rb +40 -0
- data/lib/howitzer/meta/entry.rb +62 -0
- data/lib/howitzer/meta/iframe.rb +41 -0
- data/lib/howitzer/meta/section.rb +30 -0
- data/lib/howitzer/meta.rb +11 -0
- data/lib/howitzer/utils/string_extensions.rb +6 -2
- data/lib/howitzer/version.rb +1 -1
- data/lib/howitzer/web/base_section.rb +1 -1
- data/lib/howitzer/web/capybara_context_holder.rb +1 -0
- data/lib/howitzer/web/capybara_methods_proxy.rb +15 -6
- data/lib/howitzer/web/element_dsl.rb +104 -44
- data/lib/howitzer/web/iframe_dsl.rb +23 -3
- data/lib/howitzer/web/page.rb +16 -4
- data/lib/howitzer/web/page_dsl.rb +19 -7
- data/lib/howitzer/web/page_validator.rb +27 -26
- data/lib/howitzer/web/section.rb +13 -2
- data/lib/howitzer/web/section_dsl.rb +65 -30
- data/lib/howitzer.rb +66 -10
- metadata +67 -133
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -14
- data/.rspec +0 -3
- data/.rubocop.yml +0 -44
- data/.ruby-gemset +0 -1
- data/.travis.yml +0 -7
- data/Gemfile +0 -13
- data/MAINTENANCE.md +0 -32
- data/Rakefile +0 -22
- data/features/cli_help.feature +0 -31
- data/features/cli_new.feature +0 -349
- data/features/cli_unknown.feature +0 -17
- data/features/cli_update.feature +0 -178
- data/features/cli_version.feature +0 -14
- data/features/step_definitions/common_steps.rb +0 -29
- data/features/support/env.rb +0 -1
- data/features/support/transformers.rb +0 -3
- data/generators/root/templates/.gitignore +0 -21
- data/generators/root/templates/.rubocop.yml +0 -35
- data/generators/turnip/templates/.rspec +0 -1
- data/howitzer.gemspec +0 -37
- data/lib/howitzer/mail_adapters/debugmail.rb +0 -0
- data/spec/config/custom.yml +0 -9
- data/spec/spec_helper.rb +0 -72
- data/spec/support/generator_helper.rb +0 -21
- data/spec/support/logger_helper.rb +0 -13
- data/spec/support/mailgun_unit_client.rb +0 -68
- data/spec/support/shared_examples/capybara_context_holder.rb +0 -33
- data/spec/support/shared_examples/capybara_methods_proxy.rb +0 -94
- data/spec/support/shared_examples/dynamic_section_methods.rb +0 -35
- data/spec/support/shared_examples/element_dsl.rb +0 -242
- data/spec/unit/generators/base_generator_spec.rb +0 -272
- data/spec/unit/generators/config_generator_spec.rb +0 -38
- data/spec/unit/generators/cucumber_generator_spec.rb +0 -62
- data/spec/unit/generators/emails_generator_spec.rb +0 -35
- data/spec/unit/generators/prerequisites_generator_spec.rb +0 -53
- data/spec/unit/generators/root_generator_spec.rb +0 -72
- data/spec/unit/generators/rspec_generator_spec.rb +0 -36
- data/spec/unit/generators/tasks_generator_spec.rb +0 -31
- data/spec/unit/generators/turnip_generator_spec.rb +0 -52
- data/spec/unit/generators/web_generator_spec.rb +0 -52
- data/spec/unit/lib/cache_spec.rb +0 -85
- data/spec/unit/lib/capybara_helpers_spec.rb +0 -696
- data/spec/unit/lib/email_spec.rb +0 -186
- data/spec/unit/lib/howitzer_spec.rb +0 -40
- data/spec/unit/lib/init_spec.rb +0 -2
- data/spec/unit/lib/log_spec.rb +0 -122
- data/spec/unit/lib/mail_adapters/abstract_spec.rb +0 -62
- data/spec/unit/lib/mail_adapters/mailgun_spec.rb +0 -163
- data/spec/unit/lib/mailgun_api/client_spec.rb +0 -58
- data/spec/unit/lib/mailgun_api/connector_spec.rb +0 -54
- data/spec/unit/lib/mailgun_api/response_spec.rb +0 -28
- data/spec/unit/lib/utils/string_extensions_spec.rb +0 -77
- data/spec/unit/lib/web/base_section_spec.rb +0 -43
- data/spec/unit/lib/web/element_dsl_spec.rb +0 -22
- data/spec/unit/lib/web/iframe_dsl_spec.rb +0 -144
- data/spec/unit/lib/web/page_dsl_spec.rb +0 -74
- data/spec/unit/lib/web/page_spec.rb +0 -349
- data/spec/unit/lib/web/page_validator_spec.rb +0 -276
- data/spec/unit/lib/web/section_dsl_spec.rb +0 -165
- data/spec/unit/lib/web/section_spec.rb +0 -63
- data/spec/unit/version_spec.rb +0 -8
|
@@ -3,39 +3,68 @@ module Howitzer
|
|
|
3
3
|
module Web
|
|
4
4
|
# This module combines element dsl methods
|
|
5
5
|
module ElementDsl
|
|
6
|
+
# This module holds element helper methods
|
|
7
|
+
module Helpers
|
|
8
|
+
private
|
|
9
|
+
|
|
10
|
+
def lambda_args(*args, **keyword_args)
|
|
11
|
+
{
|
|
12
|
+
lambda_args: {
|
|
13
|
+
args: args,
|
|
14
|
+
keyword_args: keyword_args
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
6
20
|
include CapybaraContextHolder
|
|
21
|
+
include Helpers
|
|
7
22
|
|
|
8
|
-
def self.included(base)
|
|
23
|
+
def self.included(base) # :nodoc:
|
|
9
24
|
base.extend(ClassMethods)
|
|
10
25
|
end
|
|
11
26
|
|
|
12
|
-
|
|
27
|
+
def convert_arguments(args, options, block_args, block_options)
|
|
28
|
+
conv_args = args.map { |el| el.is_a?(Proc) ? proc_to_selector(el, block_args, block_options) : el }
|
|
29
|
+
args_options = pop_options_from_array(conv_args)
|
|
30
|
+
block_args_options = pop_options_from_array(block_args)
|
|
31
|
+
conv_options = [args_options, options, block_args_options, block_options].map do |el|
|
|
32
|
+
el.transform_keys(&:to_sym)
|
|
33
|
+
end.reduce(&:merge).except(:lambda_args)
|
|
34
|
+
[conv_args, conv_options]
|
|
35
|
+
end
|
|
13
36
|
|
|
14
|
-
def
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
37
|
+
def proc_to_selector(proc, block_args, block_options)
|
|
38
|
+
lambda_args = extract_lambda_args(block_args, block_options)
|
|
39
|
+
if lambda_args
|
|
40
|
+
if lambda_args[:keyword_args].present?
|
|
41
|
+
proc.call(*lambda_args[:args], **lambda_args[:keyword_args])
|
|
42
|
+
else
|
|
43
|
+
proc.call(*lambda_args[:args])
|
|
44
|
+
end
|
|
45
|
+
else
|
|
46
|
+
puts "WARNING! Passing lambda arguments with element options is deprecated.\n" \
|
|
47
|
+
"Please use 'lambda_args' method, for example: foo_element(lambda_args(title: 'Example'), wait: 10)"
|
|
48
|
+
proc.call(*block_args.shift(proc.arity))
|
|
19
49
|
end
|
|
20
|
-
args << options unless options.blank?
|
|
21
|
-
args
|
|
22
50
|
end
|
|
23
51
|
|
|
24
|
-
def
|
|
25
|
-
|
|
26
|
-
new_params, params_hash = extract_element_options(params)
|
|
27
|
-
[new_args, new_params, args_hash.merge(params_hash)]
|
|
52
|
+
def extract_lambda_args(block_args, block_options)
|
|
53
|
+
(block_args.first.is_a?(Hash) && block_args.first[:lambda_args]) || block_options[:lambda_args]
|
|
28
54
|
end
|
|
29
55
|
|
|
30
|
-
def
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
56
|
+
def pop_options_from_array(value)
|
|
57
|
+
if value.last.is_a?(Hash) && !value.last.key?(:lambda_args)
|
|
58
|
+
value.pop
|
|
59
|
+
else
|
|
60
|
+
{}
|
|
61
|
+
end
|
|
35
62
|
end
|
|
36
63
|
|
|
37
|
-
# This module holds element dsl methods
|
|
64
|
+
# This module holds element dsl methods
|
|
38
65
|
module ClassMethods
|
|
66
|
+
include Helpers
|
|
67
|
+
|
|
39
68
|
protected
|
|
40
69
|
|
|
41
70
|
# Creates a group of methods to interact with described HTML element(s) on page
|
|
@@ -56,6 +85,7 @@ module Howitzer
|
|
|
56
85
|
# <b>has_no_<em>element_name</em>_element?</b> - equals capybara #has_no_selector(...) method
|
|
57
86
|
# @param name [Symbol, String] an unique element name
|
|
58
87
|
# @param args [Array] original Capybara arguments. For details, see `Capybara::Node::Finders#all`.
|
|
88
|
+
# @param options [Hash] original Capybara options. For details, see `Capybara::Node::Finders#all`.
|
|
59
89
|
# @example Using in a page class
|
|
60
90
|
# class HomePage < Howitzer::Web::Page
|
|
61
91
|
# element :top_panel, '.top'
|
|
@@ -92,14 +122,14 @@ module Howitzer
|
|
|
92
122
|
# @raise [BadElementParamsError] if wrong element arguments
|
|
93
123
|
# @!visibility public
|
|
94
124
|
|
|
95
|
-
def element(name, *args)
|
|
125
|
+
def element(name, *args, **options)
|
|
96
126
|
validate_arguments!(args)
|
|
97
|
-
define_element(name, args)
|
|
98
|
-
define_elements(name, args)
|
|
99
|
-
define_wait_for_element(name, args)
|
|
100
|
-
define_within_element(name, args)
|
|
101
|
-
define_has_element(name, args)
|
|
102
|
-
define_has_no_element(name, args)
|
|
127
|
+
define_element(name, args, options)
|
|
128
|
+
define_elements(name, args, options)
|
|
129
|
+
define_wait_for_element(name, args, options)
|
|
130
|
+
define_within_element(name, args, options)
|
|
131
|
+
define_has_element(name, args, options)
|
|
132
|
+
define_has_no_element(name, args, options)
|
|
103
133
|
end
|
|
104
134
|
|
|
105
135
|
private
|
|
@@ -110,31 +140,51 @@ module Howitzer
|
|
|
110
140
|
raise Howitzer::BadElementParamsError, 'Using more than 1 proc in arguments is forbidden'
|
|
111
141
|
end
|
|
112
142
|
|
|
113
|
-
def define_element(name, args)
|
|
114
|
-
define_method("#{name}_element") do |*block_args|
|
|
115
|
-
|
|
143
|
+
def define_element(name, args, options)
|
|
144
|
+
define_method("#{name}_element") do |*block_args, **block_options|
|
|
145
|
+
conv_args, conv_options = convert_arguments(args, options, block_args, block_options)
|
|
146
|
+
if conv_options.present?
|
|
147
|
+
capybara_context.find(*conv_args, **conv_options)
|
|
148
|
+
else
|
|
149
|
+
capybara_context.find(*conv_args)
|
|
150
|
+
end
|
|
116
151
|
end
|
|
117
152
|
private "#{name}_element"
|
|
118
153
|
end
|
|
119
154
|
|
|
120
|
-
def define_elements(name, args)
|
|
121
|
-
define_method("#{name}_elements") do |*block_args|
|
|
122
|
-
|
|
155
|
+
def define_elements(name, args, options)
|
|
156
|
+
define_method("#{name}_elements") do |*block_args, **block_options|
|
|
157
|
+
conv_args, conv_options = convert_arguments(args, options, block_args, block_options)
|
|
158
|
+
if conv_options.present?
|
|
159
|
+
capybara_context.all(*conv_args, **conv_options)
|
|
160
|
+
else
|
|
161
|
+
capybara_context.all(*conv_args)
|
|
162
|
+
end
|
|
123
163
|
end
|
|
124
164
|
private "#{name}_elements"
|
|
125
165
|
end
|
|
126
166
|
|
|
127
|
-
def define_wait_for_element(name, args)
|
|
128
|
-
define_method("wait_for_#{name}_element") do |*block_args|
|
|
129
|
-
|
|
167
|
+
def define_wait_for_element(name, args, options)
|
|
168
|
+
define_method("wait_for_#{name}_element") do |*block_args, **block_options|
|
|
169
|
+
conv_args, conv_options = convert_arguments(args, options, block_args, block_options)
|
|
170
|
+
if conv_options.present?
|
|
171
|
+
capybara_context.find(*conv_args, **conv_options)
|
|
172
|
+
else
|
|
173
|
+
capybara_context.find(*conv_args)
|
|
174
|
+
end
|
|
130
175
|
return nil
|
|
131
176
|
end
|
|
132
177
|
private "wait_for_#{name}_element"
|
|
133
178
|
end
|
|
134
179
|
|
|
135
|
-
def define_within_element(name, args)
|
|
136
|
-
define_method("within_#{name}_element") do |*block_args, &block|
|
|
137
|
-
|
|
180
|
+
def define_within_element(name, args, options)
|
|
181
|
+
define_method("within_#{name}_element") do |*block_args, **block_options, &block|
|
|
182
|
+
conv_args, conv_options = convert_arguments(args, options, block_args, block_options)
|
|
183
|
+
new_scope = if conv_options.present?
|
|
184
|
+
capybara_context.find(*conv_args, **conv_options)
|
|
185
|
+
else
|
|
186
|
+
capybara_context.find(*conv_args)
|
|
187
|
+
end
|
|
138
188
|
begin
|
|
139
189
|
capybara_scopes.push(new_scope)
|
|
140
190
|
block.call
|
|
@@ -144,15 +194,25 @@ module Howitzer
|
|
|
144
194
|
end
|
|
145
195
|
end
|
|
146
196
|
|
|
147
|
-
def define_has_element(name, args)
|
|
148
|
-
define_method("has_#{name}_element?") do |*block_args|
|
|
149
|
-
|
|
197
|
+
def define_has_element(name, args, options)
|
|
198
|
+
define_method("has_#{name}_element?") do |*block_args, **block_options|
|
|
199
|
+
conv_args, conv_options = convert_arguments(args, options, block_args, block_options)
|
|
200
|
+
if conv_options.present?
|
|
201
|
+
capybara_context.has_selector?(*conv_args, **conv_options)
|
|
202
|
+
else
|
|
203
|
+
capybara_context.has_selector?(*conv_args)
|
|
204
|
+
end
|
|
150
205
|
end
|
|
151
206
|
end
|
|
152
207
|
|
|
153
|
-
def define_has_no_element(name, args)
|
|
154
|
-
define_method("has_no_#{name}_element?") do |*block_args|
|
|
155
|
-
|
|
208
|
+
def define_has_no_element(name, args, options)
|
|
209
|
+
define_method("has_no_#{name}_element?") do |*block_args, **block_options|
|
|
210
|
+
conv_args, conv_options = convert_arguments(args, options, block_args, block_options)
|
|
211
|
+
if conv_options.present?
|
|
212
|
+
capybara_context.has_no_selector?(*conv_args, **conv_options)
|
|
213
|
+
else
|
|
214
|
+
capybara_context.has_no_selector?(*conv_args)
|
|
215
|
+
end
|
|
156
216
|
end
|
|
157
217
|
end
|
|
158
218
|
end
|
|
@@ -5,7 +5,7 @@ module Howitzer
|
|
|
5
5
|
module IframeDsl
|
|
6
6
|
include CapybaraContextHolder
|
|
7
7
|
|
|
8
|
-
def self.included(base)
|
|
8
|
+
def self.included(base) # :nodoc:
|
|
9
9
|
base.extend(ClassMethods)
|
|
10
10
|
end
|
|
11
11
|
|
|
@@ -26,7 +26,7 @@ module Howitzer
|
|
|
26
26
|
|
|
27
27
|
def convert_iframe_arguments(args, params)
|
|
28
28
|
new_args = args.deep_dup
|
|
29
|
-
hash = new_args.pop.merge(params) if new_args.last.is_a?(Hash)
|
|
29
|
+
hash = new_args.pop.transform_keys(&:to_sym).merge(params.transform_keys(&:to_sym)) if new_args.last.is_a?(Hash)
|
|
30
30
|
new_args << hash if hash.present?
|
|
31
31
|
new_args
|
|
32
32
|
end
|
|
@@ -45,6 +45,7 @@ module Howitzer
|
|
|
45
45
|
# <b>has_no_<em>frame_name</em>_iframe?</b> - equals capybara #has_no_selector(...) method
|
|
46
46
|
# @param name [Symbol, String] an unique iframe name
|
|
47
47
|
# @param args [Array] original Capybara arguments. For details, see `Capybara::Session#within_frame`.
|
|
48
|
+
# @raise [NameError] if page class can not be found
|
|
48
49
|
# @example Using in a page class
|
|
49
50
|
# class FbPage < Howitzer::Web::Page
|
|
50
51
|
# element :like, :xpath, ".//*[text()='Like']"
|
|
@@ -57,8 +58,19 @@ module Howitzer
|
|
|
57
58
|
# end
|
|
58
59
|
# end
|
|
59
60
|
#
|
|
61
|
+
# module Utils
|
|
62
|
+
# class GroupFbPage < Howitzer::Web::Page
|
|
63
|
+
# end
|
|
64
|
+
# end
|
|
65
|
+
#
|
|
60
66
|
# class HomePage < Howitzer::Web::Page
|
|
61
67
|
# iframe :fb, 1
|
|
68
|
+
#
|
|
69
|
+
# # frame with explicit class declaration
|
|
70
|
+
# # iframe :fb, FbPage, 1
|
|
71
|
+
#
|
|
72
|
+
# # frame with namespace
|
|
73
|
+
# iframe :utils_group_fb
|
|
62
74
|
# end
|
|
63
75
|
#
|
|
64
76
|
# HomePage.on do
|
|
@@ -72,7 +84,10 @@ module Howitzer
|
|
|
72
84
|
|
|
73
85
|
def iframe(name, *args)
|
|
74
86
|
raise ArgumentError, 'iframe selector arguments must be specified' if args.blank?
|
|
75
|
-
|
|
87
|
+
|
|
88
|
+
klass = args.first.is_a?(Class) ? args.shift : find_matching_class(name)
|
|
89
|
+
raise NameError, "class can not be found for #{name} iframe" if klass.blank?
|
|
90
|
+
|
|
76
91
|
define_iframe(klass, name, args)
|
|
77
92
|
define_has_iframe(name, args)
|
|
78
93
|
define_has_no_iframe(name, args)
|
|
@@ -100,6 +115,11 @@ module Howitzer
|
|
|
100
115
|
capybara_context.has_no_selector?(*iframe_element_selector(args, params))
|
|
101
116
|
end
|
|
102
117
|
end
|
|
118
|
+
|
|
119
|
+
def find_matching_class(name)
|
|
120
|
+
Howitzer::Web::Page.descendants.select { |el| el.name.underscore.tr('/', '_') == "#{name}_page" }
|
|
121
|
+
.max_by { |el| el.name.count('::') }
|
|
122
|
+
end
|
|
103
123
|
end
|
|
104
124
|
end
|
|
105
125
|
end
|
data/lib/howitzer/web/page.rb
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
require 'singleton'
|
|
2
2
|
require 'rspec/expectations'
|
|
3
3
|
require 'addressable/template'
|
|
4
|
+
require 'howitzer/meta'
|
|
4
5
|
require 'howitzer/web/capybara_methods_proxy'
|
|
5
6
|
require 'howitzer/web/page_validator'
|
|
6
7
|
require 'howitzer/web/element_dsl'
|
|
@@ -13,7 +14,7 @@ module Howitzer
|
|
|
13
14
|
module Web
|
|
14
15
|
# This class represents a single web page. This is a parent class for all web pages
|
|
15
16
|
class Page
|
|
16
|
-
UnknownPage = Class.new
|
|
17
|
+
UnknownPage = Class.new # :nodoc:
|
|
17
18
|
include Singleton
|
|
18
19
|
include CapybaraMethodsProxy
|
|
19
20
|
include ElementDsl
|
|
@@ -27,6 +28,7 @@ module Howitzer
|
|
|
27
28
|
# This Ruby callback makes all inherited classes as singleton classes.
|
|
28
29
|
|
|
29
30
|
def self.inherited(subclass)
|
|
31
|
+
super
|
|
30
32
|
subclass.class_eval { include Singleton }
|
|
31
33
|
end
|
|
32
34
|
|
|
@@ -64,6 +66,7 @@ module Howitzer
|
|
|
64
66
|
page_list = matched_pages
|
|
65
67
|
return UnknownPage if page_list.count.zero?
|
|
66
68
|
return page_list.first if page_list.count == 1
|
|
69
|
+
|
|
67
70
|
raise Howitzer::AmbiguousPageMatchingError, ambiguous_page_msg(page_list)
|
|
68
71
|
end
|
|
69
72
|
|
|
@@ -76,6 +79,7 @@ module Howitzer
|
|
|
76
79
|
end_time = ::Time.now + timeout
|
|
77
80
|
until ::Time.now > end_time
|
|
78
81
|
return true if opened?
|
|
82
|
+
|
|
79
83
|
sleep(0.5)
|
|
80
84
|
end
|
|
81
85
|
raise Howitzer::IncorrectPageError, incorrect_page_msg
|
|
@@ -97,9 +101,16 @@ module Howitzer
|
|
|
97
101
|
if defined?(path_value)
|
|
98
102
|
return "#{site_value}#{Addressable::Template.new(path_value).expand(params, url_processor)}"
|
|
99
103
|
end
|
|
104
|
+
|
|
100
105
|
raise Howitzer::NoPathForPageError, "Please specify path for '#{self}' page. Example: path '/home'"
|
|
101
106
|
end
|
|
102
107
|
|
|
108
|
+
# Provides access to meta information about entities on the page
|
|
109
|
+
# @return [Meta::Entry]
|
|
110
|
+
def meta
|
|
111
|
+
@meta ||= Meta::Entry.new(self)
|
|
112
|
+
end
|
|
113
|
+
|
|
103
114
|
class << self
|
|
104
115
|
protected
|
|
105
116
|
|
|
@@ -140,12 +151,12 @@ module Howitzer
|
|
|
140
151
|
|
|
141
152
|
def incorrect_page_msg
|
|
142
153
|
"Current page: #{current_page}, expected: #{self}.\n" \
|
|
143
|
-
|
|
154
|
+
"\tCurrent url: #{current_url}\n\tCurrent title: #{instance.title}"
|
|
144
155
|
end
|
|
145
156
|
|
|
146
157
|
def ambiguous_page_msg(page_list)
|
|
147
158
|
"Current page matches more that one page class (#{page_list.join(', ')}).\n" \
|
|
148
|
-
|
|
159
|
+
"\tCurrent url: #{current_url}\n\tCurrent title: #{instance.title}"
|
|
149
160
|
end
|
|
150
161
|
end
|
|
151
162
|
|
|
@@ -153,7 +164,8 @@ module Howitzer
|
|
|
153
164
|
|
|
154
165
|
def initialize
|
|
155
166
|
check_validations_are_defined!
|
|
156
|
-
current_window.maximize if Howitzer.maximized_window &&
|
|
167
|
+
current_window.maximize if Howitzer.maximized_window &&
|
|
168
|
+
!%w[chrome headless_chrome].include?(Capybara.current_driver)
|
|
157
169
|
end
|
|
158
170
|
|
|
159
171
|
# Reloads current page in a browser
|
|
@@ -18,7 +18,7 @@ module Howitzer
|
|
|
18
18
|
# HomePage.on { expect(HomePage.given).to have_menu_section } # Bad
|
|
19
19
|
# HomePage.on { is_expected.to have_menu_section } # Good
|
|
20
20
|
|
|
21
|
-
def is_expected # rubocop:disable
|
|
21
|
+
def is_expected # rubocop:disable Naming/PredicateName
|
|
22
22
|
expect(page_klass.given)
|
|
23
23
|
end
|
|
24
24
|
|
|
@@ -29,10 +29,15 @@ module Howitzer
|
|
|
29
29
|
# * `out` method extracts an instance variable from an original context if starts from @.
|
|
30
30
|
# Otherwise it executes a method from an original context
|
|
31
31
|
|
|
32
|
-
def method_missing(name, *args, &block)
|
|
32
|
+
def method_missing(name, *args, **options, &block)
|
|
33
33
|
return super if name =~ /\A(?:be|have)_/
|
|
34
|
-
return eval_in_out_context(*args, &block) if name == :out
|
|
35
|
-
|
|
34
|
+
return eval_in_out_context(*args, **options, &block) if name == :out
|
|
35
|
+
|
|
36
|
+
if options.present?
|
|
37
|
+
page_klass.given.send(name, *args, **options, &block)
|
|
38
|
+
else
|
|
39
|
+
page_klass.given.send(name, *args, &block)
|
|
40
|
+
end
|
|
36
41
|
end
|
|
37
42
|
|
|
38
43
|
# Makes proxied methods to be evaludated and returned as a proc
|
|
@@ -44,11 +49,17 @@ module Howitzer
|
|
|
44
49
|
|
|
45
50
|
private
|
|
46
51
|
|
|
47
|
-
def eval_in_out_context(*args, &block)
|
|
52
|
+
def eval_in_out_context(*args, **options, &block)
|
|
48
53
|
return nil if args.size.zero?
|
|
54
|
+
|
|
49
55
|
name = args.shift
|
|
50
56
|
return get_outer_instance_variable(name) if name.to_s.start_with?('@')
|
|
51
|
-
|
|
57
|
+
|
|
58
|
+
if options.present?
|
|
59
|
+
outer_context.send(name, *args, **options, &block)
|
|
60
|
+
else
|
|
61
|
+
outer_context.send(name, *args, &block)
|
|
62
|
+
end
|
|
52
63
|
end
|
|
53
64
|
|
|
54
65
|
def get_outer_instance_variable(name)
|
|
@@ -58,9 +69,10 @@ module Howitzer
|
|
|
58
69
|
attr_accessor :page_klass, :outer_context
|
|
59
70
|
end
|
|
60
71
|
|
|
61
|
-
def self.included(base)
|
|
72
|
+
def self.included(base) # :nodoc:
|
|
62
73
|
base.extend(ClassMethods)
|
|
63
74
|
end
|
|
75
|
+
|
|
64
76
|
# This module holds page dsl class methods
|
|
65
77
|
module ClassMethods
|
|
66
78
|
# Allows to execute page methods in context of the page.
|
|
@@ -6,7 +6,7 @@ module Howitzer
|
|
|
6
6
|
module PageValidator
|
|
7
7
|
@validations = {}
|
|
8
8
|
|
|
9
|
-
def self.included(base)
|
|
9
|
+
def self.included(base) # :nodoc:
|
|
10
10
|
base.extend(ClassMethods)
|
|
11
11
|
end
|
|
12
12
|
|
|
@@ -21,17 +21,19 @@ module Howitzer
|
|
|
21
21
|
|
|
22
22
|
def check_validations_are_defined!
|
|
23
23
|
return if self.class.validations.present?
|
|
24
|
+
|
|
24
25
|
raise Howitzer::NoValidationError, "No any page validation was found for '#{self.class.name}' page"
|
|
25
26
|
end
|
|
26
27
|
|
|
27
28
|
# This module holds page validation class methods
|
|
28
29
|
module ClassMethods
|
|
29
30
|
# Adds validation to validation list for current page
|
|
30
|
-
# @param
|
|
31
|
-
# @param
|
|
31
|
+
# @param type [Symbol, String] a validation type. Possible values [:url, :element_presence, :title]
|
|
32
|
+
# @param pattern_or_element_name [Symbol, String, Regexp]
|
|
32
33
|
# For :url and :title validation types must be <b>Regexp</b>
|
|
33
|
-
# For :element_presence must be one of element names described for page
|
|
34
|
-
# @param
|
|
34
|
+
# For :element_presence must be one of element names described for the page
|
|
35
|
+
# @param args [Array] any arguments required to pass for a lambda selector (:element_presence type only)
|
|
36
|
+
# @param options [Hash] keyword arguments required to pass for a lambda selector (:element_presence type only)
|
|
35
37
|
# @raise [Howitzer::UnknownValidationError] if unknown validation type
|
|
36
38
|
# @raise [Howitzer::UndefinedElementError] if :element_presence validations refers to undefined element name
|
|
37
39
|
# @example
|
|
@@ -44,12 +46,23 @@ module Howitzer
|
|
|
44
46
|
# end
|
|
45
47
|
# @example
|
|
46
48
|
# class HomePage < Howitzer::Web::Page
|
|
47
|
-
# validate :element_presence, :menu_item, 'Logout'
|
|
48
|
-
# element :menu_item, :xpath, ->(
|
|
49
|
+
# validate :element_presence, :menu_item, lambda_args(text: 'Logout')
|
|
50
|
+
# element :menu_item, :xpath, ->(text:) { ".//a[.='#{text}']" }
|
|
49
51
|
# end
|
|
50
52
|
|
|
51
|
-
def validate(
|
|
52
|
-
|
|
53
|
+
def validate(type, pattern_or_element_name, *args, **options)
|
|
54
|
+
case type.to_s.to_sym
|
|
55
|
+
when :url, :title
|
|
56
|
+
if args.present? || options.present?
|
|
57
|
+
raise ArgumentError, "Additional arguments and options are not supported by '#{type}' the validator"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
send("validate_by_#{type}", pattern_or_element_name)
|
|
61
|
+
when :element_presence
|
|
62
|
+
validate_by_element_presence(pattern_or_element_name, *args, **options)
|
|
63
|
+
else
|
|
64
|
+
raise Howitzer::UnknownValidationError, "unknown '#{type}' validation type"
|
|
65
|
+
end
|
|
53
66
|
end
|
|
54
67
|
|
|
55
68
|
# Check whether current page is opened or no
|
|
@@ -60,6 +73,7 @@ module Howitzer
|
|
|
60
73
|
|
|
61
74
|
def opened?(sync: true)
|
|
62
75
|
return validations.all? { |(_, validation)| validation.call(self, sync) } if validations.present?
|
|
76
|
+
|
|
63
77
|
raise Howitzer::NoValidationError, "No any page validation was found for '#{name}' page"
|
|
64
78
|
end
|
|
65
79
|
|
|
@@ -78,17 +92,17 @@ module Howitzer
|
|
|
78
92
|
|
|
79
93
|
private
|
|
80
94
|
|
|
81
|
-
def
|
|
95
|
+
def validate_by_element_presence(element_name, *args, **options)
|
|
82
96
|
validations[:element_presence] =
|
|
83
97
|
lambda do |web_page, sync|
|
|
84
98
|
if element_name.present? && !private_method_defined?("#{element_name}_element")
|
|
85
99
|
raise(Howitzer::UndefinedElementError, ':element_presence validation refers to ' \
|
|
86
|
-
|
|
100
|
+
"undefined '#{element_name}' element on '#{name}' page.")
|
|
87
101
|
end
|
|
88
102
|
if sync
|
|
89
|
-
web_page.instance.public_send(
|
|
103
|
+
web_page.instance.public_send("has_#{element_name}_element?", *args, **options)
|
|
90
104
|
else
|
|
91
|
-
!web_page.instance.public_send(
|
|
105
|
+
!web_page.instance.public_send("has_no_#{element_name}_element?", *args, **options)
|
|
92
106
|
end
|
|
93
107
|
end
|
|
94
108
|
end
|
|
@@ -102,19 +116,6 @@ module Howitzer
|
|
|
102
116
|
validations[:title] =
|
|
103
117
|
->(web_page, sync) { sync ? web_page.instance.has_title?(pattern) : pattern === web_page.instance.title }
|
|
104
118
|
end
|
|
105
|
-
|
|
106
|
-
def validate_by_type(type, value, additional_value)
|
|
107
|
-
case type.to_s.to_sym
|
|
108
|
-
when :url
|
|
109
|
-
validate_by_url(value)
|
|
110
|
-
when :element_presence
|
|
111
|
-
validate_element(value, additional_value)
|
|
112
|
-
when :title
|
|
113
|
-
validate_by_title(value)
|
|
114
|
-
else
|
|
115
|
-
raise Howitzer::UnknownValidationError, "unknown '#{type}' validation type"
|
|
116
|
-
end
|
|
117
|
-
end
|
|
118
119
|
end
|
|
119
120
|
end
|
|
120
121
|
end
|
data/lib/howitzer/web/section.rb
CHANGED
|
@@ -1,25 +1,36 @@
|
|
|
1
1
|
require 'howitzer/web/base_section'
|
|
2
|
+
require 'howitzer/meta'
|
|
2
3
|
|
|
3
4
|
module Howitzer
|
|
4
5
|
module Web
|
|
5
6
|
# This class uses for named sections which possible to reuse in different pages
|
|
6
7
|
class Section < BaseSection
|
|
8
|
+
# Provides access to meta information about entities in section
|
|
9
|
+
# @return [Meta::Entry]
|
|
10
|
+
def meta
|
|
11
|
+
@meta ||= Meta::Entry.new(self)
|
|
12
|
+
end
|
|
13
|
+
|
|
7
14
|
class << self
|
|
8
15
|
protected
|
|
9
16
|
|
|
10
17
|
# DSL method which specifies section container selector represented by HTML element.
|
|
11
18
|
# Any elements described in sections will start in this HTML element.
|
|
12
19
|
# @param args [Array] original Capybara arguments. For details, see `Capybara::Node::Finders#all.
|
|
20
|
+
# @param options [Array] original Capybara options. For details, see `Capybara::Node::Finders#all.
|
|
13
21
|
# @raise [ArgumentError] if no arguments were passed
|
|
14
22
|
# @example
|
|
15
23
|
# class MenuSection < Howitzer::Web::Section
|
|
16
|
-
# me :xpath, ".//*[@id='panel']"
|
|
24
|
+
# me :xpath, ".//*[@id='panel']",
|
|
17
25
|
# end
|
|
18
26
|
# @!visibility public
|
|
19
27
|
|
|
20
|
-
def me(*args)
|
|
28
|
+
def me(*args, **options)
|
|
21
29
|
raise ArgumentError, 'Finder arguments are missing' if args.blank?
|
|
30
|
+
|
|
22
31
|
@default_finder_args = args
|
|
32
|
+
@default_finder_options = options
|
|
33
|
+
self
|
|
23
34
|
end
|
|
24
35
|
end
|
|
25
36
|
end
|