howitzer 2.2.0 → 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.
Files changed (124) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +16 -1
  3. data/LICENSE +1 -1
  4. data/README.md +16 -16
  5. data/generators/base_generator.rb +1 -1
  6. data/generators/config/config_generator.rb +1 -4
  7. data/generators/config/templates/boot.rb +1 -1
  8. data/generators/config/templates/capybara.rb +1 -3
  9. data/generators/config/templates/default.yml +5 -19
  10. data/generators/config/templates/drivers/browserstack.rb +1 -1
  11. data/generators/config/templates/drivers/crossbrowsertesting.rb +1 -1
  12. data/generators/config/templates/drivers/headless_chrome.rb +1 -1
  13. data/generators/config/templates/drivers/headless_firefox.rb +1 -1
  14. data/generators/config/templates/drivers/sauce.rb +1 -1
  15. data/generators/config/templates/drivers/selenium.rb +1 -1
  16. data/generators/config/templates/drivers/selenium_grid.rb +2 -2
  17. data/generators/config/templates/drivers/testingbot.rb +1 -1
  18. data/generators/cucumber/templates/env.rb +1 -1
  19. data/generators/cucumber/templates/hooks.rb +8 -2
  20. data/generators/cucumber/templates/transformers.rb +1 -1
  21. data/generators/prerequisites/templates/factory_bot.rb +1 -1
  22. data/generators/root/templates/Gemfile.erb +4 -10
  23. data/generators/rspec/templates/spec_helper.rb +4 -4
  24. data/generators/turnip/templates/spec_helper.rb +4 -4
  25. data/generators/web/templates/example_page.rb +1 -1
  26. data/lib/howitzer/cache.rb +1 -1
  27. data/lib/howitzer/capybara_helpers.rb +27 -7
  28. data/lib/howitzer/email.rb +2 -2
  29. data/lib/howitzer/exceptions.rb +21 -21
  30. data/lib/howitzer/gmail_api/client.rb +13 -4
  31. data/lib/howitzer/log.rb +6 -6
  32. data/lib/howitzer/mailgun_api/client.rb +3 -2
  33. data/lib/howitzer/mailgun_api/response.rb +1 -2
  34. data/lib/howitzer/mailtrap_api/client.rb +1 -1
  35. data/lib/howitzer/meta/actions.rb +13 -16
  36. data/lib/howitzer/meta/element.rb +12 -10
  37. data/lib/howitzer/utils/string_extensions.rb +6 -2
  38. data/lib/howitzer/version.rb +1 -1
  39. data/lib/howitzer/web/base_section.rb +1 -1
  40. data/lib/howitzer/web/capybara_methods_proxy.rb +11 -5
  41. data/lib/howitzer/web/element_dsl.rb +104 -45
  42. data/lib/howitzer/web/iframe_dsl.rb +2 -2
  43. data/lib/howitzer/web/page.rb +4 -14
  44. data/lib/howitzer/web/page_dsl.rb +15 -6
  45. data/lib/howitzer/web/page_validator.rb +25 -26
  46. data/lib/howitzer/web/section.rb +5 -2
  47. data/lib/howitzer/web/section_dsl.rb +64 -30
  48. data/lib/howitzer.rb +2 -2
  49. metadata +24 -161
  50. data/.coveralls.yml +0 -1
  51. data/.gitignore +0 -14
  52. data/.rspec +0 -3
  53. data/.rubocop.yml +0 -60
  54. data/.ruby-gemset +0 -1
  55. data/.travis.yml +0 -8
  56. data/Gemfile +0 -14
  57. data/ISSUE_TEMPLATE.md +0 -16
  58. data/MAINTENANCE.md +0 -32
  59. data/Rakefile +0 -38
  60. data/features/cli_help.feature +0 -31
  61. data/features/cli_new.feature +0 -393
  62. data/features/cli_unknown.feature +0 -17
  63. data/features/cli_update.feature +0 -223
  64. data/features/cli_version.feature +0 -14
  65. data/features/step_definitions/common_steps.rb +0 -34
  66. data/features/support/env.rb +0 -1
  67. data/generators/config/templates/drivers/poltergeist.rb +0 -11
  68. data/generators/config/templates/drivers/webkit.rb +0 -6
  69. data/generators/root/templates/.gitignore +0 -21
  70. data/generators/root/templates/.rubocop.yml.erb +0 -56
  71. data/generators/turnip/templates/.rspec +0 -1
  72. data/howitzer.gemspec +0 -39
  73. data/lib/howitzer/mail_adapters/debugmail.rb +0 -0
  74. data/spec/config/custom.yml +0 -9
  75. data/spec/spec_helper.rb +0 -73
  76. data/spec/support/generator_helper.rb +0 -21
  77. data/spec/support/logger_helper.rb +0 -13
  78. data/spec/support/shared_examples/capybara_context_holder.rb +0 -33
  79. data/spec/support/shared_examples/capybara_methods_proxy.rb +0 -94
  80. data/spec/support/shared_examples/dynamic_section_methods.rb +0 -35
  81. data/spec/support/shared_examples/element_dsl.rb +0 -242
  82. data/spec/support/shared_examples/meta_highlight_xpath.rb +0 -41
  83. data/spec/unit/generators/base_generator_spec.rb +0 -283
  84. data/spec/unit/generators/config_generator_spec.rb +0 -61
  85. data/spec/unit/generators/cucumber_generator_spec.rb +0 -62
  86. data/spec/unit/generators/emails_generator_spec.rb +0 -35
  87. data/spec/unit/generators/prerequisites_generator_spec.rb +0 -53
  88. data/spec/unit/generators/root_generator_spec.rb +0 -86
  89. data/spec/unit/generators/rspec_generator_spec.rb +0 -36
  90. data/spec/unit/generators/tasks_generator_spec.rb +0 -31
  91. data/spec/unit/generators/templates/cucumber_spec.rb +0 -97
  92. data/spec/unit/generators/templates/rspec_spec.rb +0 -88
  93. data/spec/unit/generators/templates/turnip_spec.rb +0 -98
  94. data/spec/unit/generators/turnip_generator_spec.rb +0 -52
  95. data/spec/unit/generators/web_generator_spec.rb +0 -52
  96. data/spec/unit/lib/cache_spec.rb +0 -85
  97. data/spec/unit/lib/capybara_helpers_spec.rb +0 -730
  98. data/spec/unit/lib/email_spec.rb +0 -186
  99. data/spec/unit/lib/gmail_api/client_spec.rb +0 -26
  100. data/spec/unit/lib/howitzer_spec.rb +0 -92
  101. data/spec/unit/lib/init_spec.rb +0 -2
  102. data/spec/unit/lib/log_spec.rb +0 -122
  103. data/spec/unit/lib/mail_adapters/abstract_spec.rb +0 -62
  104. data/spec/unit/lib/mail_adapters/gmail_spec.rb +0 -128
  105. data/spec/unit/lib/mail_adapters/mailgun_spec.rb +0 -158
  106. data/spec/unit/lib/mail_adapters/mailtrap_spec.rb +0 -130
  107. data/spec/unit/lib/mailgun_api/client_spec.rb +0 -80
  108. data/spec/unit/lib/mailgun_api/connector_spec.rb +0 -54
  109. data/spec/unit/lib/mailgun_api/response_spec.rb +0 -28
  110. data/spec/unit/lib/mailtrap_api/client_spec.rb +0 -67
  111. data/spec/unit/lib/meta/element_spec.rb +0 -59
  112. data/spec/unit/lib/meta/entry_spec.rb +0 -77
  113. data/spec/unit/lib/meta/iframe_spec.rb +0 -66
  114. data/spec/unit/lib/meta/section_spec.rb +0 -43
  115. data/spec/unit/lib/utils/string_extensions_spec.rb +0 -77
  116. data/spec/unit/lib/web/base_section_spec.rb +0 -43
  117. data/spec/unit/lib/web/element_dsl_spec.rb +0 -31
  118. data/spec/unit/lib/web/iframe_dsl_spec.rb +0 -203
  119. data/spec/unit/lib/web/page_dsl_spec.rb +0 -74
  120. data/spec/unit/lib/web/page_spec.rb +0 -385
  121. data/spec/unit/lib/web/page_validator_spec.rb +0 -276
  122. data/spec/unit/lib/web/section_dsl_spec.rb +0 -165
  123. data/spec/unit/lib/web/section_spec.rb +0 -70
  124. data/spec/unit/version_spec.rb +0 -8
@@ -1,11 +1,16 @@
1
- require 'gmail'
2
-
3
1
  module Howitzer
4
2
  module GmailApi
5
3
  # A GmailApi::Client object is used to communicate with the Gmail API.
6
4
  class Client
5
+ def self.load_gmail_gem!
6
+ require 'gmail'
7
+ rescue LoadError
8
+ raise LoadError, "Unable to load `gmail` library, please add following code to your Gemfile:\n\ngem 'gmail'"
9
+ end
10
+ load_gmail_gem!
11
+
7
12
  def initialize
8
- @client = Gmail.connect(Howitzer.gmail_login, Howitzer.gmail_password)
13
+ self.client = Gmail.connect(Howitzer.gmail_login, Howitzer.gmail_password)
9
14
  end
10
15
 
11
16
  # Finds message according to given parameters
@@ -15,8 +20,12 @@ module Howitzer
15
20
  # @return [Gmail::Message] gmail message object
16
21
 
17
22
  def find_message(recipient, subject)
18
- @client.inbox.emails(to: recipient, subject: subject).last
23
+ client.inbox.emails(to: recipient, subject: subject).last
19
24
  end
25
+
26
+ private
27
+
28
+ attr_accessor :client
20
29
  end
21
30
  end
22
31
  end
data/lib/howitzer/log.rb CHANGED
@@ -80,13 +80,13 @@ module Howitzer
80
80
  Logger['ruby_log'].trace = true
81
81
  end
82
82
 
83
- #:nocov:
83
+ # :nocov:
84
84
  def log_without_formatting
85
85
  self.base_formatter = blank_formatter
86
86
  yield
87
87
  self.base_formatter = default_formatter
88
88
  end
89
- #:nocov:
89
+ # :nocov:
90
90
 
91
91
  def console_log
92
92
  StdoutOutputter.new(:console).tap { |o| o.only_at(INFO, DEBUG, WARN) }
@@ -96,13 +96,13 @@ module Howitzer
96
96
  StderrOutputter.new(:error, 'level' => ERROR)
97
97
  end
98
98
 
99
- #:nocov:
99
+ # :nocov:
100
100
  def blank_formatter
101
101
  PatternFormatter.new(pattern: '%m')
102
102
  end
103
- #:nocov:
103
+ # :nocov:
104
104
 
105
- #:nocov:
105
+ # :nocov:
106
106
  def default_formatter
107
107
  params = if Howitzer.hide_datetime_from_log
108
108
  { pattern: '[%l] %m' }
@@ -111,7 +111,7 @@ module Howitzer
111
111
  end
112
112
  PatternFormatter.new(params)
113
113
  end
114
- #:nocov:
114
+ # :nocov:
115
115
 
116
116
  def base_formatter=(formatter)
117
117
  @logger.outputters.each { |outputter| outputter.formatter = formatter }
@@ -9,9 +9,10 @@ module Howitzer
9
9
  # wrapper around RestClient so you don't have to worry about the HTTP aspect
10
10
  # of communicating with Mailgun API.
11
11
  class Client
12
- USER_AGENT = 'mailgun-sdk-ruby'.freeze #:nodoc:
12
+ USER_AGENT = 'mailgun-sdk-ruby'.freeze # :nodoc:
13
13
  attr_reader :api_user, :api_key, :api_host, :api_version, :ssl
14
- def initialize(api_user: 'api', api_key:, api_host: 'api.mailgun.net', api_version: 'v3', ssl: true)
14
+
15
+ def initialize(api_user: 'api', api_key: 'key', api_host: 'api.mailgun.net', api_version: 'v3', ssl: true)
15
16
  @api_user = api_user
16
17
  @api_key = api_key
17
18
  @api_host = api_host
@@ -6,8 +6,7 @@ module Howitzer
6
6
  # by the Client request. The Response object supports deserialization of
7
7
  # the JSON result.
8
8
  class Response
9
- attr_accessor :body
10
- attr_accessor :code
9
+ attr_accessor :body, :code
11
10
 
12
11
  def initialize(response)
13
12
  @body = response.body
@@ -5,7 +5,7 @@ module Howitzer
5
5
  module MailtrapApi
6
6
  # A Mailtrap::Client object is used to communicate with the Mailtrap API.
7
7
  class Client
8
- BASE_URL = "https://mailtrap.io/api/v1/inboxes/#{Howitzer.mailtrap_inbox_id}".freeze #:nodoc:
8
+ BASE_URL = "https://mailtrap.io/api/v1/inboxes/#{Howitzer.mailtrap_inbox_id}".freeze # :nodoc:
9
9
 
10
10
  def initialize
11
11
  @api_token = Howitzer.mailtrap_api_token
@@ -3,23 +3,26 @@ module Howitzer
3
3
  # Module with utility actions for elements
4
4
  module Actions
5
5
  # Highlights element with red border on the page
6
- # @param args [Array] arguments for elements described with lambda locators and
7
- # inline options for element/s as a hash
8
- def highlight(*args)
9
- if xpath(*args).blank?
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
10
  Howitzer::Log.debug("Element #{name} not found on the page")
11
11
  return
12
12
  end
13
- context.execute_script("document.evaluate('#{escape(xpath(*args))}', document, null, " \
14
- 'XPathResult.FIRST_ORDERED_NODE_TYPE, null).singleNodeValue.style.border = "thick solid red"')
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
+ )
15
18
  end
16
19
 
17
20
  # Returns xpath for the element
18
- # @param args [Array] arguments for elements described with lambda locators and
19
- # inline options for element/s as a hash
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`
20
23
  # @return [String, nil]
21
- def xpath(*args)
22
- capybara_element(*args).try(:path)
24
+ def xpath(*args, **options)
25
+ capybara_element(*args, **options).try(:path)
23
26
  end
24
27
 
25
28
  private
@@ -27,12 +30,6 @@ module Howitzer
27
30
  def escape(xpath)
28
31
  xpath.gsub(/(['"])/, '\\\\\\1')
29
32
  end
30
-
31
- def convert_args(args)
32
- new_args = []
33
- params = args.reject { |v| new_args << v if v.is_a?(Hash) }
34
- [params, new_args.reduce(:merge)].flatten
35
- end
36
33
  end
37
34
  end
38
35
  end
@@ -14,22 +14,24 @@ module Howitzer
14
14
  end
15
15
 
16
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 and
18
- # inline options for element/s as a hash
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
19
  # @return [Array]
20
- def capybara_elements(*args)
21
- context.send("#{name}_elements", *args)
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
22
26
  end
23
27
 
24
28
  # Finds element on the page and returns as a capybara element
25
- # @param args [Array] arguments for elements described with lambda locators and
26
- # inline options for element/s as a hash
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`
27
31
  # @param wait [Integer] wait time for element search
28
32
  # @return [Capybara::Node::Element, nil]
29
- def capybara_element(*args, wait: 0)
30
- args << { match: :first, wait: wait }
31
- args = convert_args(args)
32
- context.send("#{name}_element", *args)
33
+ def capybara_element(*args, wait: 0, **options)
34
+ context.send("#{name}_element", *args, **options.merge(match: :first, wait: wait))
33
35
  rescue Capybara::ElementNotFound
34
36
  nil
35
37
  end
@@ -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
- as_page_class.open(*args)
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
@@ -1,4 +1,4 @@
1
1
  # This module holds howitzer version
2
2
  module Howitzer
3
- VERSION = '2.2.0'.freeze #:nodoc:
3
+ VERSION = '2.3.0'.freeze # :nodoc:
4
4
  end
@@ -15,7 +15,7 @@ module Howitzer
15
15
  attr_reader :parent
16
16
 
17
17
  class << self
18
- attr_reader :default_finder_args
18
+ attr_reader :default_finder_args, :default_finder_options
19
19
  end
20
20
 
21
21
  def initialize(parent, context)
@@ -3,7 +3,7 @@ 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
- #:nocov:
6
+ # :nocov:
7
7
  class Capybara::Selenium::Driver # rubocop:disable Style/ClassAndModuleChildren
8
8
  #
9
9
  # https://github.com/teamcapybara/capybara/issues/1845
@@ -13,7 +13,7 @@ class Capybara::Selenium::Driver # rubocop:disable Style/ClassAndModuleChildren
13
13
  find_xpath('/html/head/title').map { |n| n[:text] }.first.to_s
14
14
  end
15
15
 
16
- # Known issue, works differently for phantomjs and real browsers
16
+ # Known issue, works differently for real browsers
17
17
  # https://github.com/seleniumhq/selenium/issues/1727
18
18
  def current_url
19
19
  return browser.current_url unless within_frame?
@@ -27,13 +27,13 @@ class Capybara::Selenium::Driver # rubocop:disable Style/ClassAndModuleChildren
27
27
  !(@frame_handles.blank? || @frame_handles[browser.window_handle].blank?)
28
28
  end
29
29
  end
30
- #:nocov:
30
+ # :nocov:
31
31
 
32
32
  module Howitzer
33
33
  module Web
34
34
  # This module proxies required original capybara methods to recipient
35
35
  module CapybaraMethodsProxy
36
- PROXIED_CAPYBARA_METHODS = Capybara::Session::SESSION_METHODS + #:nodoc:
36
+ PROXIED_CAPYBARA_METHODS = Capybara::Session::SESSION_METHODS + # :nodoc:
37
37
  Capybara::Session::MODAL_METHODS +
38
38
  %i[driver text]
39
39
 
@@ -41,7 +41,13 @@ module Howitzer
41
41
  # Instead of including Capybara::DSL module, we proxy most interesting Capybara methods and
42
42
  # prevent using extra methods which can potentially broke main principles and framework concept
43
43
  PROXIED_CAPYBARA_METHODS.each do |method|
44
- define_method(method) { |*args, &block| Capybara.current_session.send(method, *args, &block) }
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
45
51
  end
46
52
 
47
53
  # Accepts or declines JS alert box by given flag
@@ -3,40 +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) #:nodoc:
23
+ def self.included(base) # :nodoc:
9
24
  base.extend(ClassMethods)
10
25
  end
11
26
 
12
- private
13
-
14
- def convert_arguments(args, params)
15
- args, params, options = merge_element_options(args, params)
16
- args = args.map do |el|
17
- next(el) unless el.is_a?(Proc)
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
18
36
 
19
- el.call(*params.shift(el.arity))
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))
20
49
  end
21
- args << options unless options.blank?
22
- args
23
50
  end
24
51
 
25
- def merge_element_options(args, params)
26
- new_args, args_hash = extract_element_options(args)
27
- new_params, params_hash = extract_element_options(params)
28
- [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]
29
54
  end
30
55
 
31
- def extract_element_options(args)
32
- new_args = args.deep_dup
33
- args_hash = {}
34
- args_hash = new_args.pop if new_args.last.is_a?(Hash)
35
- [new_args, args_hash]
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
36
62
  end
37
63
 
38
- # This module holds element dsl methods methods
64
+ # This module holds element dsl methods
39
65
  module ClassMethods
66
+ include Helpers
67
+
40
68
  protected
41
69
 
42
70
  # Creates a group of methods to interact with described HTML element(s) on page
@@ -57,6 +85,7 @@ module Howitzer
57
85
  # <b>has_no_<em>element_name</em>_element?</b> - equals capybara #has_no_selector(...) method
58
86
  # @param name [Symbol, String] an unique element name
59
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`.
60
89
  # @example Using in a page class
61
90
  # class HomePage < Howitzer::Web::Page
62
91
  # element :top_panel, '.top'
@@ -93,14 +122,14 @@ module Howitzer
93
122
  # @raise [BadElementParamsError] if wrong element arguments
94
123
  # @!visibility public
95
124
 
96
- def element(name, *args)
125
+ def element(name, *args, **options)
97
126
  validate_arguments!(args)
98
- define_element(name, args)
99
- define_elements(name, args)
100
- define_wait_for_element(name, args)
101
- define_within_element(name, args)
102
- define_has_element(name, args)
103
- 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)
104
133
  end
105
134
 
106
135
  private
@@ -111,31 +140,51 @@ module Howitzer
111
140
  raise Howitzer::BadElementParamsError, 'Using more than 1 proc in arguments is forbidden'
112
141
  end
113
142
 
114
- def define_element(name, args)
115
- define_method("#{name}_element") do |*block_args|
116
- capybara_context.find(*convert_arguments(args, block_args))
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
117
151
  end
118
152
  private "#{name}_element"
119
153
  end
120
154
 
121
- def define_elements(name, args)
122
- define_method("#{name}_elements") do |*block_args|
123
- capybara_context.all(*convert_arguments(args, block_args))
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
124
163
  end
125
164
  private "#{name}_elements"
126
165
  end
127
166
 
128
- def define_wait_for_element(name, args)
129
- define_method("wait_for_#{name}_element") do |*block_args|
130
- capybara_context.find(*convert_arguments(args, block_args))
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
131
175
  return nil
132
176
  end
133
177
  private "wait_for_#{name}_element"
134
178
  end
135
179
 
136
- def define_within_element(name, args)
137
- define_method("within_#{name}_element") do |*block_args, &block|
138
- new_scope = capybara_context.find(*convert_arguments(args, block_args))
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
139
188
  begin
140
189
  capybara_scopes.push(new_scope)
141
190
  block.call
@@ -145,15 +194,25 @@ module Howitzer
145
194
  end
146
195
  end
147
196
 
148
- def define_has_element(name, args)
149
- define_method("has_#{name}_element?") do |*block_args|
150
- capybara_context.has_selector?(*convert_arguments(args, block_args))
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
151
205
  end
152
206
  end
153
207
 
154
- def define_has_no_element(name, args)
155
- define_method("has_no_#{name}_element?") do |*block_args|
156
- capybara_context.has_no_selector?(*convert_arguments(args, block_args))
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
157
216
  end
158
217
  end
159
218
  end
@@ -5,7 +5,7 @@ module Howitzer
5
5
  module IframeDsl
6
6
  include CapybaraContextHolder
7
7
 
8
- def self.included(base) #:nodoc:
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
@@ -14,7 +14,7 @@ module Howitzer
14
14
  module Web
15
15
  # This class represents a single web page. This is a parent class for all web pages
16
16
  class Page
17
- UnknownPage = Class.new #:nodoc:
17
+ UnknownPage = Class.new # :nodoc:
18
18
  include Singleton
19
19
  include CapybaraMethodsProxy
20
20
  include ElementDsl
@@ -28,6 +28,7 @@ module Howitzer
28
28
  # This Ruby callback makes all inherited classes as singleton classes.
29
29
 
30
30
  def self.inherited(subclass)
31
+ super
31
32
  subclass.class_eval { include Singleton }
32
33
  end
33
34
 
@@ -40,7 +41,6 @@ module Howitzer
40
41
 
41
42
  def self.open(validate: true, url_processor: nil, **params)
42
43
  url = expanded_url(params, url_processor)
43
- modify_user_agent if Howitzer.user_agent.present?
44
44
  Howitzer::Log.info "Open #{name} page by '#{url}' url"
45
45
  retryable(tries: 2, logger: Howitzer::Log, trace: true, on: Exception) do |retries|
46
46
  Howitzer::Log.info 'Retry...' unless retries.zero?
@@ -151,22 +151,12 @@ module Howitzer
151
151
 
152
152
  def incorrect_page_msg
153
153
  "Current page: #{current_page}, expected: #{self}.\n" \
154
- "\tCurrent url: #{current_url}\n\tCurrent title: #{instance.title}"
154
+ "\tCurrent url: #{current_url}\n\tCurrent title: #{instance.title}"
155
155
  end
156
156
 
157
157
  def ambiguous_page_msg(page_list)
158
158
  "Current page matches more that one page class (#{page_list.join(', ')}).\n" \
159
- "\tCurrent url: #{current_url}\n\tCurrent title: #{instance.title}"
160
- end
161
-
162
- def modify_user_agent
163
- driver = Capybara.current_session.driver
164
- case Howitzer.driver.to_sym
165
- when CapybaraHelpers::POLTERGEIST
166
- driver.add_headers('User-Agent' => Howitzer.user_agent)
167
- when CapybaraHelpers::WEBKIT
168
- driver.header('User-Agent', Howitzer.user_agent)
169
- end
159
+ "\tCurrent url: #{current_url}\n\tCurrent title: #{instance.title}"
170
160
  end
171
161
  end
172
162
 
@@ -29,11 +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
34
+ return eval_in_out_context(*args, **options, &block) if name == :out
35
35
 
36
- page_klass.given.send(name, *args, &block)
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
37
41
  end
38
42
 
39
43
  # Makes proxied methods to be evaludated and returned as a proc
@@ -45,13 +49,17 @@ module Howitzer
45
49
 
46
50
  private
47
51
 
48
- def eval_in_out_context(*args, &block)
52
+ def eval_in_out_context(*args, **options, &block)
49
53
  return nil if args.size.zero?
50
54
 
51
55
  name = args.shift
52
56
  return get_outer_instance_variable(name) if name.to_s.start_with?('@')
53
57
 
54
- outer_context.send(name, *args, &block)
58
+ if options.present?
59
+ outer_context.send(name, *args, **options, &block)
60
+ else
61
+ outer_context.send(name, *args, &block)
62
+ end
55
63
  end
56
64
 
57
65
  def get_outer_instance_variable(name)
@@ -61,9 +69,10 @@ module Howitzer
61
69
  attr_accessor :page_klass, :outer_context
62
70
  end
63
71
 
64
- def self.included(base) #:nodoc:
72
+ def self.included(base) # :nodoc:
65
73
  base.extend(ClassMethods)
66
74
  end
75
+
67
76
  # This module holds page dsl class methods
68
77
  module ClassMethods
69
78
  # Allows to execute page methods in context of the page.