howitzer 2.1.1 → 2.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -5
- data/CHANGELOG.md +44 -1
- data/LICENSE +1 -1
- data/README.md +19 -17
- data/generators/base_generator.rb +5 -7
- data/generators/config/config_generator.rb +3 -4
- data/generators/config/templates/boot.rb +2 -2
- data/generators/config/templates/capybara.rb +4 -5
- data/generators/config/templates/default.yml +21 -11
- data/generators/config/templates/drivers/browserstack.rb +31 -4
- data/generators/config/templates/drivers/crossbrowsertesting.rb +9 -11
- data/generators/config/templates/drivers/headless_chrome.rb +1 -1
- data/generators/config/templates/drivers/headless_firefox.rb +23 -0
- data/generators/config/templates/drivers/lambdatest.rb +30 -0
- data/generators/config/templates/drivers/sauce.rb +30 -2
- data/generators/config/templates/drivers/selenium.rb +1 -1
- data/generators/config/templates/drivers/selenium_grid.rb +3 -3
- data/generators/config/templates/drivers/testingbot.rb +29 -2
- data/generators/cucumber/templates/cuke_sniffer.rake +2 -2
- data/generators/cucumber/templates/env.rb +8 -0
- data/generators/cucumber/templates/hooks.rb +9 -3
- data/generators/cucumber/templates/transformers.rb +1 -1
- data/generators/prerequisites/templates/factory_bot.rb +2 -1
- data/generators/root/root_generator.rb +1 -1
- data/generators/root/templates/Gemfile.erb +4 -10
- data/generators/rspec/templates/spec_helper.rb +6 -5
- data/generators/turnip/templates/spec_helper.rb +6 -5
- data/generators/web/templates/example_page.rb +1 -1
- data/lib/howitzer/cache.rb +20 -19
- data/lib/howitzer/capybara_helpers.rb +59 -14
- data/lib/howitzer/email.rb +3 -2
- data/lib/howitzer/exceptions.rb +21 -21
- data/lib/howitzer/gmail_api/client.rb +13 -4
- data/lib/howitzer/log.rb +6 -6
- data/lib/howitzer/mail_adapters/gmail.rb +3 -0
- data/lib/howitzer/mail_adapters/mailgun.rb +3 -1
- data/lib/howitzer/mail_adapters/mailtrap.rb +3 -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 +1 -1
- 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 +4 -2
- data/lib/howitzer/web/page.rb +14 -14
- data/lib/howitzer/web/page_dsl.rb +18 -6
- 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 +40 -0
- metadata +31 -157
- data/.coveralls.yml +0 -1
- data/.gitignore +0 -14
- data/.rspec +0 -3
- data/.rubocop.yml +0 -51
- data/.ruby-gemset +0 -1
- data/.travis.yml +0 -7
- data/Gemfile +0 -14
- data/ISSUE_TEMPLATE.md +0 -16
- data/MAINTENANCE.md +0 -32
- data/Rakefile +0 -38
- data/features/cli_help.feature +0 -31
- data/features/cli_new.feature +0 -389
- data/features/cli_unknown.feature +0 -17
- data/features/cli_update.feature +0 -218
- data/features/cli_version.feature +0 -14
- data/features/step_definitions/common_steps.rb +0 -34
- data/features/support/env.rb +0 -1
- data/generators/config/templates/drivers/phantomjs.rb +0 -19
- data/generators/config/templates/drivers/poltergeist.rb +0 -11
- data/generators/config/templates/drivers/webkit.rb +0 -6
- 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 -39
- data/lib/howitzer/mail_adapters/debugmail.rb +0 -0
- data/spec/config/custom.yml +0 -9
- data/spec/spec_helper.rb +0 -73
- data/spec/support/generator_helper.rb +0 -21
- data/spec/support/logger_helper.rb +0 -13
- 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 -283
- data/spec/unit/generators/config_generator_spec.rb +0 -59
- 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 -75
- 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 -697
- data/spec/unit/lib/email_spec.rb +0 -186
- data/spec/unit/lib/gmail_api/client_spec.rb +0 -26
- data/spec/unit/lib/howitzer_spec.rb +0 -69
- 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/gmail_spec.rb +0 -128
- data/spec/unit/lib/mail_adapters/mailgun_spec.rb +0 -158
- data/spec/unit/lib/mail_adapters/mailtrap_spec.rb +0 -130
- data/spec/unit/lib/mailgun_api/client_spec.rb +0 -80
- 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/mailtrap_api/client_spec.rb +0 -67
- 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 -203
- data/spec/unit/lib/web/page_dsl_spec.rb +0 -74
- data/spec/unit/lib/web/page_spec.rb +0 -378
- 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
@@ -0,0 +1,35 @@
|
|
1
|
+
module Howitzer
|
2
|
+
module Meta
|
3
|
+
# Module with utility actions for elements
|
4
|
+
module Actions
|
5
|
+
# Highlights element with red border on the page
|
6
|
+
# @param args [Array] arguments for elements described with lambda locators
|
7
|
+
# @param options [Hash] original Capybara options. For details, see `Capybara::Node::Finders#all`
|
8
|
+
def highlight(*args, **options)
|
9
|
+
if xpath(*args, **options).blank?
|
10
|
+
Howitzer::Log.debug("Element #{name} not found on the page")
|
11
|
+
return
|
12
|
+
end
|
13
|
+
element = escape(xpath(*args, **options))
|
14
|
+
context.execute_script(
|
15
|
+
"document.evaluate('#{element}', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE, null)." \
|
16
|
+
'singleNodeValue.style.border = "thick solid red"'
|
17
|
+
)
|
18
|
+
end
|
19
|
+
|
20
|
+
# Returns xpath for the element
|
21
|
+
# @param args [Array] arguments for elements described with lambda locators
|
22
|
+
# @param options [Hash] original Capybara options. For details, see `Capybara::Node::Finders#all`
|
23
|
+
# @return [String, nil]
|
24
|
+
def xpath(*args, **options)
|
25
|
+
capybara_element(*args, **options).try(:path)
|
26
|
+
end
|
27
|
+
|
28
|
+
private
|
29
|
+
|
30
|
+
def escape(xpath)
|
31
|
+
xpath.gsub(/(['"])/, '\\\\\\1')
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module Howitzer
|
2
|
+
module Meta
|
3
|
+
# This class represents element entity within howitzer meta information interface
|
4
|
+
class Element
|
5
|
+
attr_reader :name, :context
|
6
|
+
|
7
|
+
include Howitzer::Meta::Actions
|
8
|
+
# Creates new meta element with meta information and utility actions
|
9
|
+
# @param name [String] name of the element
|
10
|
+
# @param context [Howitzer::Web::Page] page element belongs to
|
11
|
+
def initialize(name, context)
|
12
|
+
@name = name
|
13
|
+
@context = context
|
14
|
+
end
|
15
|
+
|
16
|
+
# Finds all instances of element on the page and returns them as array of capybara elements
|
17
|
+
# @param args [Array] arguments for elements described with lambda locators
|
18
|
+
# @param options [Hash] original Capybara options. For details, see `Capybara::Node::Finders#all`
|
19
|
+
# @return [Array]
|
20
|
+
def capybara_elements(*args, **options)
|
21
|
+
if options.present?
|
22
|
+
context.send("#{name}_elements", *args, **options)
|
23
|
+
else
|
24
|
+
context.send("#{name}_elements", *args)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
# Finds element on the page and returns as a capybara element
|
29
|
+
# @param args [Array] arguments for elements described with lambda locators
|
30
|
+
# @param options [Hash] original Capybara options. For details, see `Capybara::Node::Finders#all`
|
31
|
+
# @param wait [Integer] wait time for element search
|
32
|
+
# @return [Capybara::Node::Element, nil]
|
33
|
+
def capybara_element(*args, wait: 0, **options)
|
34
|
+
context.send("#{name}_element", *args, **options.merge(match: :first, wait: wait))
|
35
|
+
rescue Capybara::ElementNotFound
|
36
|
+
nil
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,62 @@
|
|
1
|
+
module Howitzer
|
2
|
+
module Meta
|
3
|
+
# This class provides access to meta information entities
|
4
|
+
class Entry
|
5
|
+
attr_reader :context
|
6
|
+
|
7
|
+
# Creates new meta entry instance for the page which provides access to elements, iframes and sections
|
8
|
+
# @param context [Howitzer::Web::Page] page for which entry is created
|
9
|
+
def initialize(context)
|
10
|
+
@context = context
|
11
|
+
end
|
12
|
+
|
13
|
+
# Returns array of elements
|
14
|
+
# @return [Array]
|
15
|
+
def elements
|
16
|
+
@elements ||= context
|
17
|
+
.private_methods
|
18
|
+
.grep(/\A(?!wait_)\w+_element\z/)
|
19
|
+
.map { |el| Meta::Element.new(el.to_s.gsub('_element', ''), context) }
|
20
|
+
end
|
21
|
+
|
22
|
+
# Finds element by name
|
23
|
+
# @param name [String, Symbol] element name
|
24
|
+
# @return [Meta::Element]
|
25
|
+
def element(name)
|
26
|
+
elements.find { |el| el.name == name.to_s }
|
27
|
+
end
|
28
|
+
|
29
|
+
# Returns array of sections
|
30
|
+
# @return [Array]
|
31
|
+
def sections
|
32
|
+
@sections ||= context
|
33
|
+
.public_methods
|
34
|
+
.grep(/\A(?!wait_)\w+_section$\z/)
|
35
|
+
.map { |el| Meta::Section.new(el.to_s.gsub('_section', ''), context) }
|
36
|
+
end
|
37
|
+
|
38
|
+
# Finds section by name
|
39
|
+
# @param name [String, Symbol] section name
|
40
|
+
# @return [Meta::Section]
|
41
|
+
def section(name)
|
42
|
+
sections.find { |el| el.name == name.to_s }
|
43
|
+
end
|
44
|
+
|
45
|
+
# Returns array of iframes
|
46
|
+
# @return [Array]
|
47
|
+
def iframes
|
48
|
+
@iframes ||= context
|
49
|
+
.public_methods
|
50
|
+
.grep(/\A(?!wait_)\w+_iframe$\z/)
|
51
|
+
.map { |el| Meta::Iframe.new(el.to_s.gsub('_iframe', ''), context) }
|
52
|
+
end
|
53
|
+
|
54
|
+
# Finds iframe by name
|
55
|
+
# @param name [String, Symbol] iframe name
|
56
|
+
# @return [Meta::Iframe]
|
57
|
+
def iframe(name)
|
58
|
+
iframes.find { |el| el.name == name.to_s }
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
@@ -0,0 +1,41 @@
|
|
1
|
+
module Howitzer
|
2
|
+
module Meta
|
3
|
+
# This class represents iframe entity within howitzer meta information interface
|
4
|
+
class Iframe
|
5
|
+
attr_reader :name, :context
|
6
|
+
|
7
|
+
include Howitzer::Meta::Actions
|
8
|
+
|
9
|
+
# Creates new meta iframe element with meta information and utility actions
|
10
|
+
# @param name [String] name of the iframe
|
11
|
+
# @param context [Howitzer::Web::Page] page which has this iframe
|
12
|
+
def initialize(name, context)
|
13
|
+
@name = name
|
14
|
+
@context = context
|
15
|
+
end
|
16
|
+
|
17
|
+
# Finds all instances of iframe on the page and returns them as array of capybara elements
|
18
|
+
# @return [Array]
|
19
|
+
def capybara_elements
|
20
|
+
context.capybara_context.all("iframe[src='#{site_value}']")
|
21
|
+
end
|
22
|
+
|
23
|
+
# Finds iframe on the page and returns as a capybara element
|
24
|
+
# @param wait [Integer] wait time for element search
|
25
|
+
# @return [Capybara::Node::Element, nil]
|
26
|
+
def capybara_element(wait: 0)
|
27
|
+
context.capybara_context.find("iframe[src='#{site_value}']", match: :first, wait: wait)
|
28
|
+
rescue Capybara::ElementNotFound
|
29
|
+
nil
|
30
|
+
end
|
31
|
+
|
32
|
+
# Returns url value for iframe
|
33
|
+
# @return [String]
|
34
|
+
def site_value
|
35
|
+
return @site_value if @site_value.present?
|
36
|
+
|
37
|
+
context.send("#{name}_iframe") { |frame| @site_value = frame.class.send(:site_value) }
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Howitzer
|
2
|
+
module Meta
|
3
|
+
# This class represents section entity within howitzer meta information interface
|
4
|
+
class Section
|
5
|
+
attr_reader :name, :context
|
6
|
+
|
7
|
+
include Howitzer::Meta::Actions
|
8
|
+
# Creates meta section element with meta information and utility actions
|
9
|
+
# @param name [String] name of the section
|
10
|
+
# @param context [Howitzer::Web::Page] page which has this section
|
11
|
+
def initialize(name, context)
|
12
|
+
@name = name
|
13
|
+
@context = context
|
14
|
+
end
|
15
|
+
|
16
|
+
# Finds all instances of section on the page and returns them as array of capybara elements
|
17
|
+
# @return [Array]
|
18
|
+
def capybara_elements
|
19
|
+
context.send("#{name}_sections").map(&:capybara_context)
|
20
|
+
end
|
21
|
+
|
22
|
+
# Finds section on the page and returns as a capybara element
|
23
|
+
# @return [Capybara::Node::Element, nil]
|
24
|
+
def capybara_element
|
25
|
+
section = context.send("#{name}_sections").first
|
26
|
+
section.try(:capybara_context)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,11 @@
|
|
1
|
+
module Howitzer
|
2
|
+
# This module holds meta-information about elements on the page
|
3
|
+
module Meta
|
4
|
+
end
|
5
|
+
end
|
6
|
+
|
7
|
+
require 'howitzer/meta/actions'
|
8
|
+
require 'howitzer/meta/element'
|
9
|
+
require 'howitzer/meta/section'
|
10
|
+
require 'howitzer/meta/iframe'
|
11
|
+
require 'howitzer/meta/entry'
|
@@ -7,8 +7,12 @@ module Howitzer
|
|
7
7
|
# 'home'.open #=> HomePage.open
|
8
8
|
# @see Howitzer::Web::Page.open
|
9
9
|
|
10
|
-
def open(*args)
|
11
|
-
|
10
|
+
def open(*args, **options)
|
11
|
+
if options.present?
|
12
|
+
as_page_class.open(*args, **options)
|
13
|
+
else
|
14
|
+
as_page_class.open(*args)
|
15
|
+
end
|
12
16
|
end
|
13
17
|
|
14
18
|
# Returns an instantiated page by name
|
data/lib/howitzer/version.rb
CHANGED
@@ -3,19 +3,21 @@ require 'active_support'
|
|
3
3
|
require 'active_support/core_ext'
|
4
4
|
|
5
5
|
# Remove this monkey patch after fixing the bugs in selenium-webdriver / capybara
|
6
|
-
|
6
|
+
# :nocov:
|
7
7
|
class Capybara::Selenium::Driver # rubocop:disable Style/ClassAndModuleChildren
|
8
8
|
#
|
9
9
|
# https://github.com/teamcapybara/capybara/issues/1845
|
10
10
|
def title
|
11
11
|
return browser.title unless within_frame?
|
12
|
+
|
12
13
|
find_xpath('/html/head/title').map { |n| n[:text] }.first.to_s
|
13
14
|
end
|
14
15
|
|
15
|
-
# Known issue, works differently for
|
16
|
+
# Known issue, works differently for real browsers
|
16
17
|
# https://github.com/seleniumhq/selenium/issues/1727
|
17
18
|
def current_url
|
18
19
|
return browser.current_url unless within_frame?
|
20
|
+
|
19
21
|
execute_script('return document.location.href')
|
20
22
|
end
|
21
23
|
|
@@ -25,13 +27,13 @@ class Capybara::Selenium::Driver # rubocop:disable Style/ClassAndModuleChildren
|
|
25
27
|
!(@frame_handles.blank? || @frame_handles[browser.window_handle].blank?)
|
26
28
|
end
|
27
29
|
end
|
28
|
-
|
30
|
+
# :nocov:
|
29
31
|
|
30
32
|
module Howitzer
|
31
33
|
module Web
|
32
34
|
# This module proxies required original capybara methods to recipient
|
33
35
|
module CapybaraMethodsProxy
|
34
|
-
PROXIED_CAPYBARA_METHODS = Capybara::Session::SESSION_METHODS +
|
36
|
+
PROXIED_CAPYBARA_METHODS = Capybara::Session::SESSION_METHODS + # :nodoc:
|
35
37
|
Capybara::Session::MODAL_METHODS +
|
36
38
|
%i[driver text]
|
37
39
|
|
@@ -39,7 +41,13 @@ module Howitzer
|
|
39
41
|
# Instead of including Capybara::DSL module, we proxy most interesting Capybara methods and
|
40
42
|
# prevent using extra methods which can potentially broke main principles and framework concept
|
41
43
|
PROXIED_CAPYBARA_METHODS.each do |method|
|
42
|
-
define_method(method)
|
44
|
+
define_method(method) do |*args, **options, &block|
|
45
|
+
if options.present?
|
46
|
+
Capybara.current_session.send(method, *args, **options, &block)
|
47
|
+
else
|
48
|
+
Capybara.current_session.send(method, *args, &block)
|
49
|
+
end
|
50
|
+
end
|
43
51
|
end
|
44
52
|
|
45
53
|
# Accepts or declines JS alert box by given flag
|
@@ -57,7 +65,8 @@ module Howitzer
|
|
57
65
|
private
|
58
66
|
|
59
67
|
def capybara_scopes
|
60
|
-
@
|
68
|
+
@capybara_scopes ||= Hash.new { |hash, key| hash[key] = [Capybara.current_session] }
|
69
|
+
@capybara_scopes[Howitzer.session_name]
|
61
70
|
end
|
62
71
|
end
|
63
72
|
end
|
@@ -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
|
@@ -84,8 +84,10 @@ module Howitzer
|
|
84
84
|
|
85
85
|
def iframe(name, *args)
|
86
86
|
raise ArgumentError, 'iframe selector arguments must be specified' if args.blank?
|
87
|
+
|
87
88
|
klass = args.first.is_a?(Class) ? args.shift : find_matching_class(name)
|
88
89
|
raise NameError, "class can not be found for #{name} iframe" if klass.blank?
|
90
|
+
|
89
91
|
define_iframe(klass, name, args)
|
90
92
|
define_has_iframe(name, args)
|
91
93
|
define_has_no_iframe(name, args)
|
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
|
|
@@ -39,7 +41,6 @@ module Howitzer
|
|
39
41
|
|
40
42
|
def self.open(validate: true, url_processor: nil, **params)
|
41
43
|
url = expanded_url(params, url_processor)
|
42
|
-
modify_user_agent if Howitzer.user_agent.present?
|
43
44
|
Howitzer::Log.info "Open #{name} page by '#{url}' url"
|
44
45
|
retryable(tries: 2, logger: Howitzer::Log, trace: true, on: Exception) do |retries|
|
45
46
|
Howitzer::Log.info 'Retry...' unless retries.zero?
|
@@ -65,6 +66,7 @@ module Howitzer
|
|
65
66
|
page_list = matched_pages
|
66
67
|
return UnknownPage if page_list.count.zero?
|
67
68
|
return page_list.first if page_list.count == 1
|
69
|
+
|
68
70
|
raise Howitzer::AmbiguousPageMatchingError, ambiguous_page_msg(page_list)
|
69
71
|
end
|
70
72
|
|
@@ -77,6 +79,7 @@ module Howitzer
|
|
77
79
|
end_time = ::Time.now + timeout
|
78
80
|
until ::Time.now > end_time
|
79
81
|
return true if opened?
|
82
|
+
|
80
83
|
sleep(0.5)
|
81
84
|
end
|
82
85
|
raise Howitzer::IncorrectPageError, incorrect_page_msg
|
@@ -98,9 +101,16 @@ module Howitzer
|
|
98
101
|
if defined?(path_value)
|
99
102
|
return "#{site_value}#{Addressable::Template.new(path_value).expand(params, url_processor)}"
|
100
103
|
end
|
104
|
+
|
101
105
|
raise Howitzer::NoPathForPageError, "Please specify path for '#{self}' page. Example: path '/home'"
|
102
106
|
end
|
103
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
|
+
|
104
114
|
class << self
|
105
115
|
protected
|
106
116
|
|
@@ -141,22 +151,12 @@ module Howitzer
|
|
141
151
|
|
142
152
|
def incorrect_page_msg
|
143
153
|
"Current page: #{current_page}, expected: #{self}.\n" \
|
144
|
-
|
154
|
+
"\tCurrent url: #{current_url}\n\tCurrent title: #{instance.title}"
|
145
155
|
end
|
146
156
|
|
147
157
|
def ambiguous_page_msg(page_list)
|
148
158
|
"Current page matches more that one page class (#{page_list.join(', ')}).\n" \
|
149
|
-
|
150
|
-
end
|
151
|
-
|
152
|
-
def modify_user_agent
|
153
|
-
driver = Capybara.current_session.driver
|
154
|
-
case Howitzer.driver.to_sym
|
155
|
-
when CapybaraHelpers::POLTERGEIST
|
156
|
-
driver.add_headers('User-Agent' => Howitzer.user_agent)
|
157
|
-
when CapybaraHelpers::WEBKIT
|
158
|
-
driver.header('User-Agent', Howitzer.user_agent)
|
159
|
-
end
|
159
|
+
"\tCurrent url: #{current_url}\n\tCurrent title: #{instance.title}"
|
160
160
|
end
|
161
161
|
end
|
162
162
|
|