howitzer 1.0.1 → 1.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 (81) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -1
  3. data/.travis.yml +3 -2
  4. data/CHANGELOG.md +25 -5
  5. data/GETTING_STARTED.md +158 -13
  6. data/README.md +49 -32
  7. data/Rakefile +10 -1
  8. data/bin/howitzer +49 -78
  9. data/features/cli_help.feature +30 -0
  10. data/features/cli_new.feature +263 -0
  11. data/features/cli_unknown.feature +17 -0
  12. data/features/cli_version.feature +14 -0
  13. data/features/step_definitions/common_steps.rb +1 -0
  14. data/features/support/env.rb +1 -0
  15. data/features/support/transformers.rb +3 -0
  16. data/generators/base_generator.rb +2 -0
  17. data/generators/config/config_generator.rb +1 -1
  18. data/generators/config/templates/default.yml +4 -14
  19. data/generators/cucumber/cucumber_generator.rb +1 -1
  20. data/generators/cucumber/templates/cucumber.yml +1 -2
  21. data/generators/cucumber/templates/env.rb +8 -7
  22. data/generators/emails/emails_generator.rb +1 -1
  23. data/generators/pages/pages_generator.rb +1 -1
  24. data/generators/pages/templates/example_page.rb +2 -2
  25. data/generators/root/root_generator.rb +1 -1
  26. data/generators/root/templates/Gemfile +1 -1
  27. data/generators/rspec/rspec_generator.rb +1 -1
  28. data/generators/rspec/templates/example_spec.rb +2 -2
  29. data/generators/rspec/templates/rspec.rake +1 -1
  30. data/generators/rspec/templates/spec_helper.rb +6 -5
  31. data/generators/tasks/tasks_generator.rb +1 -1
  32. data/generators/tasks/templates/common.rake +1 -0
  33. data/howitzer.gemspec +8 -8
  34. data/lib/howitzer.rb +4 -1
  35. data/lib/howitzer/blank_page.rb +6 -0
  36. data/lib/howitzer/capybara/dsl_ex.rb +15 -0
  37. data/lib/howitzer/capybara/settings.rb +267 -0
  38. data/lib/howitzer/email.rb +134 -0
  39. data/lib/howitzer/exceptions.rb +18 -0
  40. data/lib/howitzer/helpers.rb +34 -23
  41. data/lib/howitzer/init.rb +1 -4
  42. data/lib/howitzer/mailgun/client.rb +48 -0
  43. data/lib/howitzer/mailgun/connector.rb +34 -0
  44. data/lib/howitzer/mailgun/response.rb +28 -0
  45. data/lib/howitzer/utils.rb +2 -2
  46. data/lib/howitzer/utils/data_generator/data_storage.rb +15 -2
  47. data/lib/howitzer/utils/data_generator/gen.rb +14 -10
  48. data/lib/howitzer/utils/locator_store.rb +14 -7
  49. data/lib/howitzer/utils/log.rb +2 -0
  50. data/lib/howitzer/utils/page_validator.rb +74 -27
  51. data/lib/howitzer/version.rb +1 -1
  52. data/lib/howitzer/web_page.rb +83 -32
  53. data/spec/config/default.yml +10 -12
  54. data/spec/spec_helper.rb +12 -0
  55. data/spec/support/mailgun_unit_client.rb +60 -0
  56. data/spec/unit/generators/generators_spec.rb +7 -7
  57. data/spec/unit/lib/capybara/dsl_ex_spec.rb +60 -0
  58. data/spec/unit/lib/{capybara_settings_spec.rb → capybara/settings_spec.rb} +16 -10
  59. data/spec/unit/lib/email_spec.rb +129 -0
  60. data/spec/unit/lib/helpers_spec.rb +160 -34
  61. data/spec/unit/lib/init_spec.rb +1 -12
  62. data/spec/unit/lib/mailgun/client_spec.rb +36 -0
  63. data/spec/unit/lib/mailgun/connector_spec.rb +70 -0
  64. data/spec/unit/lib/mailgun/response_spec.rb +29 -0
  65. data/spec/unit/lib/utils/data_generator/data_storage_spec.rb +23 -5
  66. data/spec/unit/lib/utils/data_generator/gen_spec.rb +2 -63
  67. data/spec/unit/lib/utils/locator_store_spec.rb +41 -6
  68. data/spec/unit/lib/utils/log_spec.rb +1 -1
  69. data/spec/unit/lib/utils/page_validator_spec.rb +149 -25
  70. data/spec/unit/lib/web_page_spec.rb +127 -53
  71. metadata +102 -142
  72. data/lib/howitzer/utils/capybara_patched.rb +0 -23
  73. data/lib/howitzer/utils/capybara_settings.rb +0 -247
  74. data/lib/howitzer/utils/email/email.rb +0 -85
  75. data/lib/howitzer/utils/email/mail_client.rb +0 -132
  76. data/lib/howitzer/utils/email/mailgun.rb +0 -175
  77. data/lib/howitzer/utils/email/mailgun_helper.rb +0 -61
  78. data/spec/unit/bin/howitzer_spec.rb +0 -175
  79. data/spec/unit/lib/utils/email/email_spec.rb +0 -75
  80. data/spec/unit/lib/utils/email/mail_client_spec.rb +0 -115
  81. data/spec/unit/lib/utils/email/mailgun_helper_spec.rb +0 -95
@@ -1,3 +1,5 @@
1
+ require 'howitzer/exceptions'
2
+
1
3
  #The following are locator aliases:
2
4
  #
3
5
  #1) locator
@@ -18,8 +20,6 @@
18
20
 
19
21
 
20
22
  module LocatorStore
21
- BadLocatorParamsError = Class.new(StandardError)
22
- LocatorNotDefinedError = Class.new(StandardError)
23
23
 
24
24
  def self.included(base)
25
25
  base.extend(ClassMethods)
@@ -27,8 +27,6 @@ module LocatorStore
27
27
 
28
28
  module ClassMethods
29
29
  LOCATOR_TYPES = [:base, :link, :field, :button]
30
- class BadLocatorParamsError < StandardError; end
31
- class LocatorNotSpecifiedError < StandardError; end
32
30
 
33
31
  ##
34
32
  #
@@ -157,6 +155,15 @@ module LocatorStore
157
155
  end
158
156
  end
159
157
 
158
+ def first_element(name)
159
+ type, locator = find_locator(name)
160
+ if type == :base
161
+ send :first, locator
162
+ else
163
+ send :first, type, locator
164
+ end
165
+ end
166
+
160
167
  protected
161
168
 
162
169
  def find_locator(name)
@@ -164,7 +171,7 @@ module LocatorStore
164
171
  LOCATOR_TYPES.each do|type|
165
172
  return [type, locator_by_type(type, name)] if (@locators || {}).fetch(self.name, {}).fetch(type, {})[name]
166
173
  end
167
- raise(LocatorNotDefinedError, name)
174
+ log.error(Howitzer::LocatorNotDefinedError, name)
168
175
  end
169
176
 
170
177
  # looks up locator in current and all super classes
@@ -180,7 +187,7 @@ module LocatorStore
180
187
 
181
188
  def locator_by_type(type, name)
182
189
  locator = parent_locator(type, name)
183
- raise(LocatorNotDefinedError, name) if locator.nil?
190
+ log.error(Howitzer::LocatorNotDefinedError, name) if locator.nil?
184
191
  locator
185
192
  end
186
193
 
@@ -188,7 +195,7 @@ module LocatorStore
188
195
  @locators ||= {}
189
196
  @locators[self.name] ||= {}
190
197
  @locators[self.name][type] ||= {}
191
- raise BadLocatorParamsError, args.inspect if params.nil? || (!params.is_a?(Proc) && params.empty?)
198
+ log.error Howitzer::BadLocatorParamsError, args.inspect if params.nil? || (!params.is_a?(Proc) && params.empty?)
192
199
  case params.class.name
193
200
  when 'Hash'
194
201
  @locators[self.name][type][name] = [params.keys.first.to_sym, params.values.first.to_s]
@@ -45,7 +45,9 @@ module Howitzer
45
45
  exception = args.first.new(args[1])
46
46
  exception.set_backtrace(args.last)
47
47
  exception
48
+ #:nocov:
48
49
  else nil
50
+ #:nocov:
49
51
  end
50
52
  end
51
53
  err_backtrace = object.backtrace ? "\n\t#{object.backtrace.join("\n\t")}" : nil
@@ -1,11 +1,14 @@
1
+ require 'howitzer/exceptions'
2
+
1
3
  module Howitzer
2
4
  module Utils
3
5
  module PageValidator
4
- WrongOptionError = Class.new(StandardError)
5
- NoValidationError = Class.new(StandardError)
6
- UnknownValidationName = Class.new(StandardError)
7
6
  @validations = {}
8
7
 
8
+ def self.included(base) #:nodoc:
9
+ base.extend(ClassMethods)
10
+ end
11
+
9
12
  ##
10
13
  #
11
14
  # Returns validation list
@@ -16,20 +19,40 @@ module Howitzer
16
19
  @validations
17
20
  end
18
21
 
19
- def self.included(base) #:nodoc:
20
- base.extend(ClassMethods)
22
+ ##
23
+ #
24
+ # Returns page list
25
+ #
26
+ # @return [Array]
27
+ #
28
+ def self.pages
29
+ @pages ||= []
21
30
  end
22
31
 
23
32
  ##
33
+ # Check if any validations are defined, if no, tries to find old style, else raise error
24
34
  #
25
- # Checks that correct page has been loaded
26
- #
27
- # @raise [Howitzer::Utils::PageValidator::NoValidationError] If no validation was specified
35
+ # @raise [Howitzer::NoValidationError] If no one validation is defined for page
28
36
  #
29
- def check_correct_page_loaded
30
- validations = PageValidator.validations[self.class.name]
31
- raise NoValidationError, "No any page validation was found" if validations.nil?
32
- validations.each {|(_, validation)| validation.call(self)}
37
+
38
+ def check_validations_are_defined!
39
+ if validations.nil? && !old_url_validation_present?
40
+ log.error Howitzer::NoValidationError, "No any page validation was found for '#{self.class.name}' page"
41
+ end
42
+ end
43
+
44
+ private
45
+
46
+ def validations
47
+ PageValidator.validations[self.class.name]
48
+ end
49
+
50
+ def old_url_validation_present?
51
+ if self.class.const_defined?("URL_PATTERN")
52
+ self.class.validates :url, pattern: self.class.const_get("URL_PATTERN")
53
+ warn "[Deprecated] Old style page validation is using. Please use new style:\n\t validates :url, pattern: URL_PATTERN"
54
+ true
55
+ end
33
56
  end
34
57
 
35
58
  module ClassMethods
@@ -42,41 +65,65 @@ module Howitzer
42
65
  # @option options [Hash] Validation options
43
66
  # :pattern => [Regexp] For :url and :title validation types
44
67
  # :locator => [String] For :element_presence (Existing locator name)
45
- # @raise [Howitzer::Utils::PageValidator::UnknownValidationName] If unknown validation type was passed
68
+ # @raise [Howitzer::UnknownValidationError] If unknown validation type was passed
46
69
  #
47
70
  def validates(name, options)
48
- raise TypeError, "Expected options to be Hash, actual is '#{options.class}'" unless options.class == Hash
71
+ log.error TypeError, "Expected options to be Hash, actual is '#{options.class}'" unless options.class == Hash
49
72
  PageValidator.validations[self.name] ||= {}
50
73
  case name.to_sym
51
74
  when :url
52
- validate_url options
75
+ validate_by_pattern(:url, options)
53
76
  when :element_presence
54
77
  validate_element options
55
78
  when :title
56
- validate_title options
79
+ validate_by_pattern(:title, options)
57
80
  else
58
- raise UnknownValidationName, "unknown '#{name}' validation name"
81
+ log.error Howitzer::UnknownValidationError, "unknown '#{name}' validation name"
59
82
  end
60
83
  end
61
84
 
62
- private
85
+ ##
86
+ # Check whether page is opened or no
87
+ #
88
+ # @raise [Howitzer::NoValidationError] If no one validation is defined for page
89
+ #
90
+ # *Returns:*
91
+ # * +boolean+
92
+ #
63
93
 
64
- def validate_url(options)
65
- pattern = options[:pattern] || options["pattern"]
66
- raise WrongOptionError, "Please specify ':pattern' option as Regexp object" if pattern.nil? || !pattern.is_a?(Regexp)
67
- PageValidator.validations[self.name][:url] = lambda { |web_page| web_page.wait_for_url(pattern) }
94
+ def opened?
95
+ validation_list = PageValidator.validations[self.name]
96
+ if validation_list.blank?
97
+ log.error Howitzer::NoValidationError, "No any page validation was found for '#{self.name}' page"
98
+ else
99
+ !validation_list.any? {|(_, validation)| !validation.call(self)}
100
+ end
68
101
  end
69
102
 
103
+ ##
104
+ #
105
+ # Finds all matched pages which are satisfy of defined validations
106
+ #
107
+ # *Returns:*
108
+ # * +array+ - page names
109
+ #
110
+
111
+ def matched_pages
112
+ PageValidator.pages.select{|klass| klass.opened? }
113
+ end
114
+
115
+ private
116
+
70
117
  def validate_element(options)
71
118
  locator = options[:locator] || options["locator"]
72
- raise WrongOptionError, "Please specify ':locator' option as one of page locator names" if locator.nil? || locator.empty?
73
- PageValidator.validations[self.name][:element_presence] = lambda { |web_page| web_page.find_element(locator) }
119
+ log.error Howitzer::WrongOptionError, "Please specify ':locator' option as one of page locator names" if locator.nil? || locator.empty?
120
+ PageValidator.validations[self.name][:element_presence] = lambda { |web_page| web_page.first_element(locator) }
74
121
  end
75
122
 
76
- def validate_title(options)
123
+ def validate_by_pattern(name, options)
77
124
  pattern = options[:pattern] || options["pattern"]
78
- raise WrongOptionError, "Please specify ':pattern' option as Regexp object" if pattern.nil? || !pattern.is_a?(Regexp)
79
- PageValidator.validations[self.name][:title] = lambda { |web_page| web_page.wait_for_title(pattern) }
125
+ log.error Howitzer::WrongOptionError, "Please specify ':pattern' option as Regexp object" if pattern.nil? || !pattern.is_a?(Regexp)
126
+ PageValidator.validations[self.name][name] = lambda { |web_page| pattern === web_page.send(name) }
80
127
  end
81
128
 
82
129
  end
@@ -1,3 +1,3 @@
1
1
  module Howitzer
2
- VERSION = "1.0.1"
2
+ VERSION = "1.0.2"
3
3
  end
@@ -1,22 +1,25 @@
1
+ require "singleton"
1
2
  require "rspec/expectations"
2
3
  require "howitzer/utils/locator_store"
3
4
  require "howitzer/utils/page_validator"
4
- require "singleton"
5
+ require "howitzer/capybara/dsl_ex"
6
+ require 'howitzer/exceptions'
5
7
 
6
8
  class WebPage
7
9
 
8
- BLANK_PAGE = 'about:blank'
9
- IncorrectPageError = Class.new(StandardError)
10
+ BLANK_PAGE = 'about:blank' # @deprecated , use BlankPage instead
11
+ UnknownPage = Class.new
10
12
 
11
13
  include LocatorStore
12
14
  include Howitzer::Utils::PageValidator
13
15
  include RSpec::Matchers
14
- include Capybara::DSL
15
- extend Capybara::DSL
16
+ include Howitzer::Capybara::DslEx
17
+ extend Howitzer::Capybara::DslEx
16
18
  include Singleton
17
19
 
18
20
  def self.inherited(subclass)
19
21
  subclass.class_eval { include Singleton }
22
+ Howitzer::Utils::PageValidator.pages << subclass
20
23
  end
21
24
 
22
25
  ##
@@ -30,7 +33,7 @@ class WebPage
30
33
  # * +WebPage+ - New instance of current class
31
34
  #
32
35
 
33
- def self.open(url="#{app_url}#{self::URL}")
36
+ def self.open(url = "#{app_url unless self == BlankPage}#{self::URL}")
34
37
  log.info "Open #{self.name} page by '#{url}' url"
35
38
  retryable(tries: 2, logger: log, trace: true, on: Exception) do |retries|
36
39
  log.info "Retry..." unless retries.zero?
@@ -48,7 +51,72 @@ class WebPage
48
51
  #
49
52
 
50
53
  def self.given
51
- self.instance.tap{ |page| page.check_correct_page_loaded }
54
+ wait_for_opened
55
+ self.instance
56
+ end
57
+
58
+ ##
59
+ #
60
+ # Returns current url
61
+ #
62
+ # *Returns:*
63
+ # * +string+ - Current url
64
+ #
65
+
66
+ def self.url
67
+ self.current_url
68
+ end
69
+
70
+ ##
71
+ #
72
+ # Returns body text of html page
73
+ #
74
+ # *Returns:*
75
+ # * +string+ - Body text
76
+ #
77
+
78
+ def self.text
79
+ page.find('body').text
80
+ end
81
+
82
+ ##
83
+ #
84
+ # Tries to identify current page name or raise error if ambiguous page matching
85
+ #
86
+ # *Returns:*
87
+ # * +string+ - page name
88
+ #
89
+
90
+ def self.current_page
91
+ page_list = matched_pages
92
+ if page_list.count.zero?
93
+ UnknownPage
94
+ elsif page_list.count > 1
95
+ log.error Howitzer::AmbiguousPageMatchingError,
96
+ "Current page matches more that one page class (#{page_list.join(', ')}).\n\tCurrent url: #{current_url}\n\tCurrent title: #{title}"
97
+ elsif page_list.count == 1
98
+ page_list.first
99
+ end
100
+ end
101
+
102
+ ##
103
+ #
104
+ # Waits until web page is not opened, or raise error after timeout
105
+ #
106
+ # *Parameters:*
107
+ # * +time_out+ - Seconds that will be waiting for web page to be loaded
108
+ #
109
+
110
+ def self.wait_for_opened(timeout=settings.timeout_small)
111
+ end_time = ::Time.now + timeout
112
+ until ::Time.now > end_time
113
+ self.opened? ? return : sleep(0.5)
114
+ end
115
+ log.error Howitzer::IncorrectPageError, "Current page: #{self.current_page}, expected: #{self}.\n\tCurrent url: #{current_url}\n\tCurrent title: #{title}"
116
+ end
117
+
118
+ def initialize
119
+ check_validations_are_defined!
52
120
  end
53
121
 
54
122
  ##
@@ -109,6 +177,7 @@ class WebPage
109
177
 
110
178
  # @deprecated
111
179
  # With Capybara 2.x it is extra
180
+ #:nocov:
112
181
  def wait_for_ajax(timeout=settings.timeout_small, message=nil)
113
182
  end_time = ::Time.now + timeout
114
183
  until ::Time.now > end_time
@@ -117,8 +186,10 @@ class WebPage
117
186
  end
118
187
  log.error message || "Timed out waiting for ajax requests to complete"
119
188
  end
189
+ #:nocov:
120
190
 
121
191
  ##
192
+ # @deprecated
122
193
  #
123
194
  # Waits until web page is loaded
124
195
  #
@@ -128,15 +199,17 @@ class WebPage
128
199
  #
129
200
 
130
201
  def wait_for_url(expected_url, timeout=settings.timeout_small)
202
+ warn "[Deprecated] This method is deprecated, and will be removed in next version of Howitzer"
131
203
  end_time = ::Time.now + timeout
132
204
  until ::Time.now > end_time
133
205
  operator = expected_url.is_a?(Regexp) ? :=~ : :==
134
206
  return true if current_url.send(operator, expected_url).tap{|res| sleep 1 unless res}
135
207
  end
136
- log.error IncorrectPageError, "Current url: #{current_url}, expected: #{expected_url}"
208
+ log.error Howitzer::IncorrectPageError, "Current url: #{current_url}, expected: #{expected_url}"
137
209
  end
138
210
 
139
211
  ##
212
+ # @deprecated
140
213
  #
141
214
  # Waits until web is loaded with expected title
142
215
  #
@@ -146,12 +219,13 @@ class WebPage
146
219
  #
147
220
 
148
221
  def wait_for_title(expected_title, timeout=settings.timeout_small)
222
+ warn "[Deprecated] This method is deprecated, and will be removed in next version of Howitzer"
149
223
  end_time = ::Time.now + timeout
150
224
  until ::Time.now > end_time
151
225
  operator = expected_title.is_a?(Regexp) ? :=~ : :==
152
226
  return true if title.send(operator, expected_title).tap{|res| sleep 1 unless res}
153
227
  end
154
- log.error IncorrectPageError, "Current title: #{title}, expected: #{expected_title}"
228
+ log.error Howitzer::IncorrectPageError, "Current title: #{title}, expected: #{expected_title}"
155
229
  end
156
230
 
157
231
  ##
@@ -164,18 +238,6 @@ class WebPage
164
238
  visit current_url
165
239
  end
166
240
 
167
- ##
168
- #
169
- # Returns current url
170
- #
171
- # *Returns:*
172
- # * +string+ - Current url
173
- #
174
-
175
- def self.current_url
176
- page.current_url
177
- end
178
-
179
241
  ##
180
242
  #
181
243
  # Returns Page title
@@ -188,15 +250,4 @@ class WebPage
188
250
  page.title
189
251
  end
190
252
 
191
- ##
192
- #
193
- # Returns body text of html page
194
- #
195
- # *Returns:*
196
- # * +string+ - Body text
197
- #
198
-
199
- def self.text
200
- page.find('body').text
201
- end
202
253
  end
@@ -12,17 +12,15 @@ hide_datetime_from_log: true
12
12
  driver: selenium
13
13
 
14
14
  ###########################################################
15
- # MAIL SETTINGS #
15
+ # MAILGUN SETTINGS #
16
16
  ###########################################################
17
- mail_smtp_server: smtp.demo.com
18
- mail_smtp_port: 25
19
- mail_smtp_domain: demo.com
20
- mail_smtp_user_name: test_user
21
- mail_smtp_user_pass: mypass
17
+ mailgun_key: mailgun_account_private_key
18
+ mailgun_domain: mailgun@test.domain
22
19
 
23
- mail_pop3_server: pop.demo.com
24
- mail_pop3_port: 995
25
- mail_pop3_timeout: 120
26
- mail_pop3_domain: ${mail_smtp_domain}
27
- mail_pop3_user_name: test_user
28
- mail_pop3_user_pass: mypass
20
+ ############################################################
21
+ # TIMEOUTS #
22
+ ############################################################
23
+ timeout_tiny: 1
24
+ timeout_short: 2
25
+ timeout_small: 5
26
+ timeout_medium: 15