howitzer 2.0.1 → 2.0.2

Sign up to get free protection for your applications and to get access to all the features.
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