howitzer 2.0.1 → 2.0.2

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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/.rubocop.yml +6 -0
  3. data/.travis.yml +4 -3
  4. data/CHANGELOG.md +18 -1
  5. data/Gemfile +1 -1
  6. data/README.md +3 -1
  7. data/Rakefile +1 -1
  8. data/bin/howitzer +3 -3
  9. data/features/cli_new.feature +2 -0
  10. data/generators/config/templates/capybara.rb +13 -11
  11. data/generators/config/templates/default.yml +2 -0
  12. data/generators/cucumber/cucumber_generator.rb +2 -1
  13. data/generators/cucumber/templates/cuke_sniffer.rake +21 -0
  14. data/generators/cucumber/templates/env.rb +6 -0
  15. data/generators/root/templates/Gemfile.erb +1 -0
  16. data/generators/rspec/templates/spec_helper.rb +2 -1
  17. data/generators/turnip/templates/spec_helper.rb +2 -1
  18. data/howitzer.gemspec +2 -1
  19. data/lib/howitzer.rb +1 -0
  20. data/lib/howitzer/capybara_helpers.rb +1 -1
  21. data/lib/howitzer/exceptions.rb +1 -0
  22. data/lib/howitzer/version.rb +1 -1
  23. data/lib/howitzer/web/capybara_methods_proxy.rb +26 -1
  24. data/lib/howitzer/web/element_dsl.rb +16 -3
  25. data/lib/howitzer/web/iframe_dsl.rb +34 -17
  26. data/lib/howitzer/web/page.rb +1 -0
  27. data/lib/howitzer/web/page_dsl.rb +2 -1
  28. data/lib/howitzer/web/page_validator.rb +5 -1
  29. data/spec/spec_helper.rb +1 -2
  30. data/spec/support/shared_examples/capybara_methods_proxy.rb +8 -8
  31. data/spec/support/shared_examples/dynamic_section_methods.rb +4 -4
  32. data/spec/support/shared_examples/element_dsl.rb +66 -53
  33. data/spec/unit/generators/cucumber_generator_spec.rb +8 -2
  34. data/spec/unit/lib/cache_spec.rb +1 -1
  35. data/spec/unit/lib/mailgun_api/client_spec.rb +6 -6
  36. data/spec/unit/lib/web/iframe_dsl_spec.rb +67 -22
  37. data/spec/unit/lib/web/page_spec.rb +25 -25
  38. data/spec/unit/lib/web/page_validator_spec.rb +37 -22
  39. data/spec/unit/lib/web/section_dsl_spec.rb +3 -3
  40. metadata +20 -5
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d0462470e4e9b727be8229a5a30f4d677daacd06
4
- data.tar.gz: 50f292514b58d7bf57b52f6dc2cc7f0dda26c2d8
3
+ metadata.gz: d72a5c40224b791c09af55a7f96a85783dd9bbec
4
+ data.tar.gz: 5c06f66b976b7fe0d389ebd516aa99b7ec60b7a5
5
5
  SHA512:
6
- metadata.gz: 6efa7374c1460bca6a650c42267525a4f2f6d1362e95cac070dc4dab101d7d3c9652a93bd898f933a0655da25856c2e281b6d2542101015329aa93a9c60d51dd
7
- data.tar.gz: 91f1a0ec11ab2fdc23d744773bb3802aefe18fff33f465e185824002f4cfa6f8c07be9722b2c967f3228bcea0952457f44061cef13bf941f545a563fddc9d61c
6
+ metadata.gz: e6540a619b0d5d157703116803ed3e3a032ce4570367c1e2dff77fcf97daf516c81fa2220c27cd2ec4a3e788768b2001cf7409d28508b6d5085e26b246652722
7
+ data.tar.gz: 46d599bfd49cb004da4b7ddb3fe79d8b765df77ae9e4ac4a4c42eb7c02aae385b1b712c22d565cb002e2742b1bc4be33b0418c4d29850ea7a620d16c070f65f0
@@ -3,6 +3,7 @@
3
3
 
4
4
  AllCops:
5
5
  TargetRubyVersion: 2.3
6
+ DisplayCopNames: true
6
7
 
7
8
  LineLength:
8
9
  Max: 120
@@ -33,3 +34,8 @@ Metrics/ModuleLength:
33
34
 
34
35
  Metrics/BlockLength:
35
36
  Enabled: false
37
+
38
+ Style/MixinGrouping:
39
+ EnforcedStyle: separated
40
+ Exclude:
41
+ - '**/*_steps.rb'
@@ -1,6 +1,7 @@
1
1
  language: ruby
2
2
  before_install:
3
3
  - gem install bundler
4
- rvm:
5
- - 2.2.5
6
- - 2.3.1
4
+ rvm:
5
+ - 2.2.6
6
+ - 2.3.3
7
+ - 2.4.0
@@ -1,9 +1,26 @@
1
- ## [In git](https://github.com/strongqa/howitzer/compare/v2.0.1...master)
1
+ ## [In git](https://github.com/strongqa/howitzer/compare/v2.0.2...master)
2
2
 
3
3
  ### New Features
4
4
 
5
5
  ### Bugfixes
6
6
 
7
+ ## [v2.0.2](https://github.com/strongqa/howitzer/compare/v2.0.1...v2.0.2)
8
+
9
+ ### New Features
10
+ - Integrate rspec-wait gem
11
+ - Support Ruby 2.4.0
12
+ - Support Selenium 3
13
+ - Integrate cuke-sniffer gem
14
+ - Activate rspec disable_monkey_patching! mode by default
15
+ - Support capybara frame options
16
+ - Add element_presence argument validation
17
+ - Review and improve tests quality
18
+
19
+ ### Bugfixes
20
+ - Fix element capybara options merging
21
+ - [#211](https://github.com/strongqa/howitzer/issues/211) Validation for iframe does not operate as intended
22
+ - [#210](https://github.com/strongqa/howitzer/issues/210) Options like "wait" can not be used with iframe methods
23
+
7
24
  ## [v2.0.1](https://github.com/strongqa/howitzer/compare/v2.0.0...v2.0.1)
8
25
 
9
26
  ### New Features
data/Gemfile CHANGED
@@ -2,9 +2,9 @@ source 'https://rubygems.org'
2
2
  # Specify your gem's dependencies in howitzer.gemspec
3
3
  group :test do
4
4
  gem 'coveralls', require: false
5
- gem 'simplecov', require: false
6
5
  gem 'repeater', require: false
7
6
  gem 'rest-client', require: false
7
+ gem 'simplecov', require: false
8
8
  end
9
9
  gemspec
10
10
 
data/README.md CHANGED
@@ -44,7 +44,9 @@ You can also find the Rdoc documentation on [Rubygems](https://rubygems.org/gems
44
44
  * [Ruby](https://www.ruby-lang.org/en/downloads/) 2.2.2+
45
45
  * [DevKit](https://github.com/oneclick/rubyinstaller/wiki/Development-Kit#installation-instructions) (For **Windows** only)
46
46
  * [PhantomJS](http://phantomjs.org/download.html) (For **phantomjs** and **poltergeist** drivers only)
47
- * [ChromeDriver](https://github.com/SeleniumHQ/selenium/wiki/ChromeDriver)
47
+ * [ChromeDriver](https://sites.google.com/a/chromium.org/chromedriver/) (For **chrome** selenium browser)
48
+ * [GeckoDriver](https://github.com/mozilla/geckodriver/releases) (For **firefox** selenium browser)
49
+ * [SafariDriver](https://webkit.org/blog/6900/webdriver-support-in-safari-10/) (For **safari** selenium browser)
48
50
  * [QT](https://github.com/thoughtbot/capybara-webkit/wiki/Installing-Qt-and-compiling-capybara-webkit) (For **webkit** driver only)
49
51
 
50
52
  ## Setup
data/Rakefile CHANGED
@@ -19,4 +19,4 @@ YARD::Rake::YardocTask.new { |_t| }
19
19
 
20
20
  RuboCop::RakeTask.new
21
21
 
22
- task default: [:rubocop, :spec, :cucumber]
22
+ task default: %i(rubocop spec cucumber)
@@ -13,13 +13,13 @@ module HowitzerCli
13
13
  arg_name '<PROJECT NAME>'
14
14
  command :new do |c|
15
15
  c.desc 'Integrate Cucumber'
16
- c.switch [:c, :cucumber], negatable: false
16
+ c.switch %i(c cucumber), negatable: false
17
17
 
18
18
  c.desc 'Integrate Rspec'
19
- c.switch [:r, :rspec], negatable: false
19
+ c.switch %i(r rspec), negatable: false
20
20
 
21
21
  c.desc 'Integrate Turnip'
22
- c.switch [:t, :turnip], negatable: false
22
+ c.switch %i(t turnip), negatable: false
23
23
 
24
24
  c.action do |_global_options, options, args|
25
25
  if !args.empty?
@@ -145,6 +145,7 @@ Feature: Howitzer CLI New Project Creation
145
145
  Added 'features/support/transformers.rb' file
146
146
  Added 'features/example.feature' file
147
147
  Added 'tasks/cucumber.rake' file
148
+ Added 'tasks/cuke_sniffer.rake' file
148
149
 
149
150
  """
150
151
  Then a directory named "test_automation" should exist
@@ -166,6 +167,7 @@ Feature: Howitzer CLI New Project Creation
166
167
  | test_automation/prerequisites/models/user.rb |
167
168
  | test_automation/tasks/common.rake |
168
169
  | test_automation/tasks/cucumber.rake |
170
+ | test_automation/tasks/cuke_sniffer.rake |
169
171
  | test_automation/Gemfile |
170
172
  | test_automation/Rakefile |
171
173
  | test_automation/.gitignore |
@@ -1,13 +1,13 @@
1
- HOWITZER_KNOWN_DRIVERS = [
2
- :selenium,
3
- :selenium_grid,
4
- :webkit,
5
- :poltergeist,
6
- :phantomjs,
7
- :sauce,
8
- :testingbot,
9
- :browserstack
10
- ].freeze
1
+ HOWITZER_KNOWN_DRIVERS = %i(
2
+ selenium
3
+ selenium_grid
4
+ webkit
5
+ poltergeist
6
+ phantomjs
7
+ sauce
8
+ testingbot
9
+ browserstack
10
+ ).freeze
11
11
 
12
12
  unless HOWITZER_KNOWN_DRIVERS.include?(Howitzer.driver.to_s.to_sym)
13
13
  raise Howitzer::UnknownDriverError, "Unknown '#{Howitzer.driver}' driver." \
@@ -83,7 +83,9 @@ Capybara.register_driver :phantomjs do |app|
83
83
  desired_capabilities: {
84
84
  javascript_enabled: !Howitzer.phantom_ignore_js_errors
85
85
  },
86
- args: ["--ignore-ssl-errors=#{Howitzer.phantom_ignore_ssl_errors ? 'yes' : 'no'}"]
86
+ driver_opts: {
87
+ args: ["--ignore-ssl-errors=#{Howitzer.phantom_ignore_ssl_errors ? 'yes' : 'no'}"]
88
+ }
87
89
  )
88
90
  end
89
91
 
@@ -36,6 +36,8 @@
36
36
  phantom_ignore_js_errors: false
37
37
  phantom_ignore_ssl_errors: true
38
38
 
39
+ rspec_wait_timeout: 10
40
+
39
41
  ###########################################################
40
42
  # Cloud-based Cross-browser Services #
41
43
  ###########################################################
@@ -11,7 +11,8 @@ module Howitzer
11
11
  { source: 'hooks.rb', destination: 'features/support/hooks.rb' },
12
12
  { source: 'transformers.rb', destination: 'features/support/transformers.rb' },
13
13
  { source: 'example.feature', destination: 'features/example.feature' },
14
- { source: 'cucumber.rake', destination: 'tasks/cucumber.rake' }
14
+ { source: 'cucumber.rake', destination: 'tasks/cucumber.rake' },
15
+ { source: 'cuke_sniffer.rake', destination: 'tasks/cuke_sniffer.rake' }
15
16
  ] }
16
17
  end
17
18
 
@@ -0,0 +1,21 @@
1
+ require 'cuke_sniffer'
2
+
3
+ def path_to_features
4
+ @_path_to_features ||= File.expand_path(File.join(__dir__, '..', 'features'))
5
+ end
6
+
7
+ def cuke_sniffer
8
+ @_cuke_sniffer ||= CukeSniffer::CLI.new(
9
+ features_location: path_to_features,
10
+ step_definitions_location: File.join(path_to_features, 'step_definitions'),
11
+ hooks_location: File.join(path_to_features, 'support'),
12
+ no_catalog: false
13
+ )
14
+ end
15
+
16
+ desc 'Generate cuke_sniffer reports'
17
+ task :cuke_sniffer do
18
+ FileUtils.mkdir_p(Howitzer.log_dir)
19
+ cuke_sniffer.output_html("#{Howitzer.log_dir}/cuke_sniffer_results.html")
20
+ cuke_sniffer.output_min_html("#{Howitzer.log_dir}/min_cuke_sniffer_results.html")
21
+ end
@@ -4,6 +4,12 @@ require_relative '../../config/boot'
4
4
  require_relative '../../config/capybara'
5
5
 
6
6
  World(FactoryGirl::Syntax::Methods)
7
+ World(RSpec::Wait)
8
+
9
+ RSpec.configure do |config|
10
+ config.wait_timeout = Howitzer.rspec_wait_timeout
11
+ config.disable_monkey_patching!
12
+ end
7
13
 
8
14
  FileUtils.mkdir_p(Howitzer.log_dir)
9
15
 
@@ -16,6 +16,7 @@ gem 'capybara-screenshot'
16
16
  <% if cucumber %>
17
17
  gem 'syntax'
18
18
  gem 'cucumber', '~>2.0'
19
+ gem 'cuke_sniffer', require: false
19
20
  <% elsif rspec %>
20
21
  gem 'rspec', '~>3.2'
21
22
  <% elsif turnip %>
@@ -12,8 +12,9 @@ RSpec.configure do |config|
12
12
 
13
13
  config.include FactoryGirl::Syntax::Methods
14
14
 
15
- config.disable_monkey_patching = true
15
+ config.disable_monkey_patching!
16
16
  config.color = true
17
+ config.wait_timeout = Howitzer.rspec_wait_timeout
17
18
 
18
19
  config.before(:each) do
19
20
  scenario_name =
@@ -11,8 +11,9 @@ RSpec.configure do |config|
11
11
 
12
12
  config.include FactoryGirl::Syntax::Methods
13
13
 
14
- config.disable_monkey_patching = true
14
+ config.disable_monkey_patching!
15
15
  config.color = true
16
+ config.wait_timeout = Howitzer.rspec_wait_timeout
16
17
 
17
18
  config.before(:each) do
18
19
  scenario_name =
@@ -26,7 +26,8 @@ Gem::Specification.new do |gem|
26
26
  gem.add_runtime_dependency 'nokogiri', '~> 1.6' if gem.platform.to_s =~ /mswin|mingw/
27
27
  gem.add_runtime_dependency 'rake'
28
28
  gem.add_runtime_dependency 'rspec', '~>3.2'
29
- gem.add_runtime_dependency 'selenium-webdriver', '< 3'
29
+ gem.add_runtime_dependency 'rspec-wait'
30
+ gem.add_runtime_dependency 'selenium-webdriver', '< 4.0'
30
31
  gem.add_runtime_dependency 'sexy_settings'
31
32
 
32
33
  gem.add_development_dependency('aruba')
@@ -1,6 +1,7 @@
1
1
  require 'selenium-webdriver'
2
2
  require 'capybara'
3
3
  require 'sexy_settings'
4
+ require 'rspec/wait'
4
5
 
5
6
  SexySettings.configure do |config|
6
7
  config.path_to_default_settings = File.expand_path('config/default.yml', Dir.pwd)
@@ -10,7 +10,7 @@ module Howitzer
10
10
  # Testingbot or Browserstack cloud service
11
11
 
12
12
  def cloud_driver?
13
- [:sauce, :testingbot, :browserstack].include?(Howitzer.driver.to_sym)
13
+ %i(sauce testingbot browserstack).include?(Howitzer.driver.to_sym)
14
14
  end
15
15
 
16
16
  # @return [Boolean] whether or not current browser is
@@ -19,4 +19,5 @@ module Howitzer
19
19
  NoPathForPageError = Class.new(StandardError) #:nodoc:
20
20
  NoEmailSubjectError = Class.new(StandardError) #:nodoc:
21
21
  NoDataError = Class.new(StandardError) #:nodoc:
22
+ UndefinedElementError = Class.new(StandardError) #:nodoc:
22
23
  end
@@ -1,4 +1,4 @@
1
1
  # This module holds howitzer version
2
2
  module Howitzer
3
- VERSION = '2.0.1'.freeze #:nodoc:
3
+ VERSION = '2.0.2'.freeze #:nodoc:
4
4
  end
@@ -1,12 +1,37 @@
1
1
  require 'capybara'
2
2
 
3
+ # Remove this monkey patch after fixing the bugs in selenium-webdriver / capybara
4
+ #:nocov:
5
+ class Capybara::Selenium::Driver # rubocop:disable Style/ClassAndModuleChildren
6
+ #
7
+ # https://github.com/teamcapybara/capybara/issues/1845
8
+ def title
9
+ return browser.title unless within_frame?
10
+ find_xpath('/html/head/title').map { |n| n[:text] }.first.to_s
11
+ end
12
+
13
+ # Known issue, works differently for phantomjs and real browsers
14
+ # https://github.com/seleniumhq/selenium/issues/1727
15
+ def current_url
16
+ return browser.current_url unless within_frame?
17
+ execute_script('return document.location.href')
18
+ end
19
+
20
+ private
21
+
22
+ def within_frame?
23
+ !(@frame_handles.empty? || @frame_handles[browser.window_handle].empty?)
24
+ end
25
+ end
26
+ #:nocov:
27
+
3
28
  module Howitzer
4
29
  module Web
5
30
  # This module proxies required original capybara methods to recipient
6
31
  module CapybaraMethodsProxy
7
32
  PROXIED_CAPYBARA_METHODS = Capybara::Session::SESSION_METHODS + #:nodoc:
8
33
  Capybara::Session::MODAL_METHODS +
9
- [:driver, :text]
34
+ %i(driver text)
10
35
 
11
36
  # Capybara form dsl methods are not compatible with page object pattern and Howitzer gem.
12
37
  # Instead of including Capybara::DSL module, we proxy most interesting Capybara methods and
@@ -12,15 +12,28 @@ module Howitzer
12
12
  private
13
13
 
14
14
  def convert_arguments(args, params)
15
- hash = params.deep_dup.pop if params.last.is_a?(Hash)
15
+ args, params, options = merge_element_options(args, params)
16
16
  args = args.map do |el|
17
17
  next(el) unless el.is_a?(Proc)
18
18
  el.call(*params.shift(el.arity))
19
19
  end
20
- args << hash unless hash.nil?
20
+ args << options unless options.blank?
21
21
  args
22
22
  end
23
23
 
24
+ def merge_element_options(args, params)
25
+ new_args, args_hash = extract_element_options(args)
26
+ new_params, params_hash = extract_element_options(params)
27
+ [new_args, new_params, args_hash.merge(params_hash)]
28
+ end
29
+
30
+ def extract_element_options(args)
31
+ new_args = args.deep_dup
32
+ args_hash = {}
33
+ args_hash = new_args.pop if new_args.last.is_a?(Hash)
34
+ [new_args, args_hash]
35
+ end
36
+
24
37
  # This module holds element dsl methods methods
25
38
  module ClassMethods
26
39
  protected
@@ -42,7 +55,7 @@ module Howitzer
42
55
  #
43
56
  # <b>has_no_<em>element_name</em>_element?</b> - equals capybara #has_no_selector(...) method
44
57
  # @param name [Symbol, String] an unique element name
45
- # @param args [Array] original Capybara arguments. For details, see `Capybara::Node::Finders#all.
58
+ # @param args [Array] original Capybara arguments. For details, see `Capybara::Node::Finders#all`.
46
59
  # @example Using in a page class
47
60
  # class HomePage < Howitzer::Web::Page
48
61
  # element :top_panel, '.top'
@@ -11,8 +11,24 @@ module Howitzer
11
11
 
12
12
  private
13
13
 
14
- def iframe_element_selector(selector)
15
- selector.is_a?(Integer) ? ["iframe:nth-of-type(#{selector + 1})"] : [:frame, selector]
14
+ def iframe_element_selector(args, params)
15
+ args = convert_iframe_arguments(args, params)
16
+ case args[0]
17
+ when String, Hash
18
+ [:frame, *args]
19
+ when Integer
20
+ idx = args.shift
21
+ ["iframe:nth-of-type(#{idx + 1})", *args]
22
+ else
23
+ args
24
+ end
25
+ end
26
+
27
+ def convert_iframe_arguments(args, params)
28
+ new_args = args.deep_dup
29
+ hash = new_args.pop.merge(params) if new_args.last.is_a?(Hash)
30
+ new_args << hash if hash.present?
31
+ new_args
16
32
  end
17
33
 
18
34
  # This module holds frame dsl class methods
@@ -28,7 +44,7 @@ module Howitzer
28
44
  #
29
45
  # <b>has_no_<em>frame_name</em>_iframe?</b> - equals capybara #has_no_selector(...) method
30
46
  # @param name [Symbol, String] an unique iframe name
31
- # @param selector [Integer, String] frame name/id or index. Possible to specify id as #some_id
47
+ # @param args [Array] original Capybara arguments. For details, see `Capybara::Session#within_frame`.
32
48
  # @example Using in a page class
33
49
  # class FbPage < Howitzer::Web::Page
34
50
  # element :like, :xpath, ".//*[text()='Like']"
@@ -54,33 +70,34 @@ module Howitzer
54
70
  # HomePage.on { is_expected.to have_fb_iframe }
55
71
  # @!visibility public
56
72
 
57
- def iframe(name, selector)
73
+ def iframe(name, *args)
74
+ raise ArgumentError, 'iframe selector arguments must be specified' if args.blank?
58
75
  klass = "#{name}_page".classify.constantize
59
- define_iframe(klass, name, selector)
60
- define_has_iframe(name, selector)
61
- define_has_no_iframe(name, selector)
76
+ define_iframe(klass, name, args)
77
+ define_has_iframe(name, args)
78
+ define_has_no_iframe(name, args)
62
79
  end
63
80
 
64
81
  private
65
82
 
66
- def define_iframe(klass, name, selector)
67
- define_method "#{name}_iframe" do |&block|
68
- iframe_selector = selector.is_a?(Integer) ? selector : selector.split('#').last
69
- capybara_context.within_frame(iframe_selector) do
83
+ def define_iframe(klass, name, args)
84
+ define_method "#{name}_iframe" do |**params, &block|
85
+ capybara_context.within_frame(*convert_iframe_arguments(args, params)) do
86
+ klass.displayed?
70
87
  block.call klass.instance
71
88
  end
72
89
  end
73
90
  end
74
91
 
75
- def define_has_iframe(name, selector)
76
- define_method("has_#{name}_iframe?") do
77
- capybara_context.has_selector?(*iframe_element_selector(selector))
92
+ def define_has_iframe(name, args)
93
+ define_method("has_#{name}_iframe?") do |**params|
94
+ capybara_context.has_selector?(*iframe_element_selector(args, params))
78
95
  end
79
96
  end
80
97
 
81
- def define_has_no_iframe(name, selector)
82
- define_method("has_no_#{name}_iframe?") do
83
- capybara_context.has_no_selector?(*iframe_element_selector(selector))
98
+ def define_has_no_iframe(name, args)
99
+ define_method("has_no_#{name}_iframe?") do |**params|
100
+ capybara_context.has_no_selector?(*iframe_element_selector(args, params))
84
101
  end
85
102
  end
86
103
  end