howitzer 2.1.1 → 2.4.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 +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
|
|