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,23 +0,0 @@
1
- module CapybaraPatched
2
- class Capybara::Selenium::PatchedDriver < Capybara::Selenium::Driver
3
- RETRY_ON = [
4
- ::Selenium::WebDriver::Error::UnhandledError,
5
- ::Selenium::WebDriver::Error::UnknownError,
6
- ::Selenium::WebDriver::Error::InvalidSelectorError
7
- ]
8
-
9
- # workaround for transferring correct arguments via call stack
10
- Capybara::Session.instance_eval do
11
- self::NODE_METHODS.each do |method|
12
- class_eval <<-RUBY
13
- def #{method}(*args, &block)
14
- retryable(tries:3, logger: log, trace: true, on: RETRY_ON) do
15
- @touched = true
16
- current_scope.send(:#{method}, *args.flatten, &block)
17
- end
18
- end
19
- RUBY
20
- end
21
- end
22
- end
23
- end
@@ -1,247 +0,0 @@
1
- ##
2
- #
3
- # Predefined Capybara settings and capybara drivers
4
- #
5
-
6
- require 'howitzer/utils/log'
7
- module CapybaraSettings
8
- extend self
9
-
10
- ##
11
- #
12
- # Predefined settings of Firefox browser
13
- #
14
- # *Returns:*
15
- # * +Hash+ - Settings that can be changed
16
- #
17
-
18
-
19
- def self.base_ff_profile_settings
20
- profile = Selenium::WebDriver::Firefox::Profile.new
21
- profile["network.http.phishy-userpass-length"] = 255
22
- profile["browser.safebrowsing.malware.enabled"] = false
23
- profile["network.automatic-ntlm-auth.allow-non-fqdn"] = true
24
- profile["network.ntlm.send-lm-response"] = true
25
- profile["network.automatic-ntlm-auth.trusted-uris"] = settings.app_host
26
- profile
27
- end
28
-
29
- class << self
30
-
31
- ##
32
- #
33
- #Defines driver based on specified test environment settings
34
- #
35
-
36
- def define_driver
37
- case settings.driver.to_sym
38
- when :selenium
39
- define_selenium_driver
40
- when :selenium_dev
41
- define_selenium_dev_driver
42
- when :webkit
43
- define_webkit_driver
44
- when :poltergeist
45
- define_poltergeist_driver
46
- when :sauce
47
- define_sauce_driver
48
- when :testingbot
49
- define_testingbot_driver
50
- else
51
- log.error "Unknown '#{settings.driver}' driver. Check your settings, it should be one of [selenium, selenium_dev, webkit, sauce, testingbot]"
52
- end
53
- end
54
-
55
- private
56
-
57
- def define_selenium_driver
58
- Capybara.register_driver :selenium do |app|
59
- params = {browser: settings.sel_browser.to_sym}
60
- params[:profile] = base_ff_profile_settings if ff_browser?
61
- Capybara::Selenium::Driver.new app, params
62
- end
63
- end
64
-
65
- def define_selenium_dev_driver
66
- Capybara.register_driver :selenium_dev do |app|
67
- profile = base_ff_profile_settings
68
- vendor_dir = settings.custom_vendor_dir || File.join(File.dirname(__FILE__), '..', 'vendor')
69
- raise "Vendor directory was not found('#{vendor_dir}')." unless Dir.exist?(vendor_dir)
70
- %w(firebug*.xpi firepath*.xpi).each do |file_name|
71
- full_path_pattern = File.join(File.expand_path(vendor_dir), file_name)
72
- if (full_path = Dir[full_path_pattern].first)
73
- profile.add_extension full_path
74
- else
75
- raise "Extension was not found by '#{full_path_pattern}' pattern!"
76
- end
77
- end
78
- profile['extensions.firebug.currentVersion'] = 'Last' # avoid 'first run' tab
79
- profile["extensions.firebug.previousPlacement"] = 1
80
- profile["extensions.firebug.onByDefault"] = true
81
- profile["extensions.firebug.defaultPanelName"] = "firepath"
82
- profile["extensions.firebug.script.enableSites"] = true
83
- profile["extensions.firebug.net.enableSites"] = true
84
- profile["extensions.firebug.console.enableSites"] = true
85
-
86
- Capybara::Selenium::Driver.new app, browser: :firefox, profile: profile
87
- end
88
- end
89
-
90
- def define_webkit_driver
91
- require 'capybara-webkit'
92
- end
93
-
94
- def define_poltergeist_driver
95
- require 'capybara/poltergeist'
96
- Capybara.register_driver :poltergeist do |app|
97
- Capybara::Poltergeist::Driver.new(
98
- app, {
99
- js_errors: !settings.pjs_ignore_js_errors,
100
- phantomjs_options: ["--ignore-ssl-errors=#{settings.pjs_ignore_ssl_errors ? 'yes' : 'no'}" ]
101
- }
102
- )
103
- end
104
- end
105
-
106
- def define_sauce_driver
107
- task_name = ENV['RAKE_TASK'].to_s.sub(/(?:r?spec|cucumber):?(.*)/, '\1').upcase
108
- caps_opts = {
109
- platform: settings.sl_platform,
110
- browser_name: settings.sl_browser_name,
111
- name: "#{ENV['RAKE_TASK'] ? (task_name.empty? ? 'ALL' : task_name) : 'CUSTOM'} #{settings.sl_browser_name.upcase}",
112
- "max-duration" => settings.sl_max_duration,
113
- 'idle-timeout' => settings.sl_idle_timeout,
114
- 'selenium-version' => settings.sl_selenium_version,
115
- 'record-screenshots' => settings.sl_record_screenshot,
116
- 'video-upload-on-pass' => settings.sl_video_upload_on_pass
117
- }
118
-
119
- unless (settings.sl_browser_version.to_s || "").empty?
120
- caps_opts['browser-version'] = settings.sl_browser_version.to_s
121
- end
122
-
123
- options = {
124
- url: settings.sl_url,
125
- desired_capabilities: Selenium::WebDriver::Remote::Capabilities.new(caps_opts),
126
- http_client: Selenium::WebDriver::Remote::Http::Default.new.tap{|c| c.timeout = settings.timeout_medium},
127
- browser: :remote
128
- }
129
-
130
- Capybara.register_driver :sauce do |app|
131
- driver = Capybara::Selenium::Driver.new(app, options)
132
- driver.browser.file_detector = lambda do |args|
133
- str = args.first.to_s
134
- str if File.exist?(str)
135
- end
136
- driver
137
- end
138
- end
139
-
140
- def define_testingbot_driver
141
- require 'testingbot'
142
- task_name = ENV['RAKE_TASK'].to_s.sub(/(?:r?spec|cucumber):?(.*)/, '\1').upcase
143
- caps_opts = {
144
- platform: settings.tb_platform,
145
- browser_name: settings.tb_browser_name,
146
- name: "#{ENV['RAKE_TASK'] ? (task_name.empty? ? 'ALL' : task_name) : 'CUSTOM'} #{settings.tb_browser_name.upcase}",
147
- maxduration: settings.tb_max_duration.to_i,
148
- idletimeout: settings.tb_idle_timeout.to_i,
149
- 'selenium-version' => settings.tb_selenium_version,
150
- screenshot: settings.tb_record_screenshot,
151
- 'avoid-proxy' => settings.tb_avoid_proxy
152
- }
153
-
154
- unless (settings.tb_browser_version.to_s || "").empty?
155
- caps_opts['version'] = settings.tb_browser_version.to_s
156
- end
157
- options = {
158
- url: settings.tb_url,
159
- desired_capabilities: Selenium::WebDriver::Remote::Capabilities.new(caps_opts),
160
- http_client: Selenium::WebDriver::Remote::Http::Default.new.tap{|c| c.timeout = settings.timeout_medium},
161
- browser: :remote
162
- }
163
- Capybara.register_driver :testingbot do |app|
164
- driver = Capybara::Selenium::Driver.new(app, options)
165
- driver.browser.file_detector = lambda do |args|
166
- str = args.first.to_s
167
- str if File.exist?(str)
168
- end
169
- driver
170
- end
171
- end
172
- end
173
-
174
- ##
175
- #
176
- # Returns url of current Sauce Labs job
177
- #
178
- # *Parameters:*
179
- # * +name+ - Your account name
180
- #
181
- # *Returns:*
182
- # * +string+ - URL address of last running Sauce Labs job
183
- #
184
-
185
- def sauce_resource_path(name)
186
- host = "https://#{settings.sl_user}:#{settings.sl_api_key}@saucelabs.com"
187
- path = "/rest/#{settings.sl_user}/jobs/#{session_id}/results/#{name}"
188
- "#{host}#{path}"
189
- end
190
-
191
- ##
192
- #
193
- # Sends http request to change current Sauce Labs job status - pass/fail
194
- #
195
- # *Parameters:*
196
- # * +json_data+ - test status as hash (for details see Saucelab documentation)
197
- #
198
-
199
-
200
- def update_sauce_job_status(json_data = {})
201
- host = "http://#{settings.sl_user}:#{settings.sl_api_key}@saucelabs.com"
202
- path = "/rest/v1/#{settings.sl_user}/jobs/#{session_id}"
203
- url = "#{host}#{path}"
204
- RestClient.put url, json_data.to_json, content_type: :json, accept: :json
205
- end
206
-
207
- ##
208
- #
209
- # Returns custom name for Sauce Labs job
210
- #
211
- # *Returns:*
212
- # * +string+ - Return name of current Sauce Labs job
213
- #
214
-
215
- def suite_name
216
- res = if ENV['RAKE_TASK']
217
- res = ENV['RAKE_TASK'].sub(/(?:r?spec|cucumber):?(.*)/, '\1').upcase
218
- res.empty? ? 'ALL' : res
219
- else
220
- 'CUSTOM'
221
- end
222
- "#{res} #{settings.sl_browser_name.upcase}"
223
- end
224
-
225
- ##
226
- #
227
- # Returns current session id
228
- #
229
-
230
- def session_id
231
- Capybara.current_session.driver.browser.instance_variable_get(:@bridge).session_id
232
- end
233
-
234
- Capybara.run_server = false
235
- Capybara.app_host = ''
236
- Capybara.default_wait_time = settings.timeout_small
237
- Capybara.ignore_hidden_elements = true
238
- Capybara.visible_text_only = true
239
- Capybara.match = :one
240
- Capybara.exact_options = true
241
- Capybara.default_driver = settings.driver.to_sym
242
- Capybara.javascript_driver = settings.driver.to_sym
243
-
244
- define_driver
245
-
246
- end
247
-
@@ -1,85 +0,0 @@
1
- require 'rspec/matchers'
2
- require 'howitzer/utils/email/mail_client'
3
-
4
- class Email
5
- include RSpec::Matchers
6
- attr_reader :recipient_address
7
-
8
- ##
9
- #
10
- # Creates new email with message
11
- #
12
- # *Parameters:*
13
- # * +message+ - email message
14
- #
15
-
16
- def initialize(message)
17
- expect(message.subject).to include(self.class::SUBJECT)
18
- @recipient_address = ::Mail::Address.new(message.to.first)
19
- @message = message
20
- end
21
-
22
- ##
23
- #
24
- # Search mail by recepient
25
- #
26
- # *Parameters:*
27
- # * +recepient+ - recepient's email address
28
- #
29
-
30
- def self.find_by_recipient(recipient)
31
- find(recipient, self::SUBJECT)
32
- end
33
-
34
- ##
35
- #
36
- # Search mail by recepient and subject.
37
- #
38
- # *Parameters:*
39
- # * +recepient+ - recepient's email address
40
- # * +subject+ - email subject
41
- #
42
-
43
- def self.find(recipient, subject)
44
- messages = MailClient.by_email(recipient).find_mail do |mail|
45
- /#{Regexp.escape(subject)}/ === mail.subject && mail.to == [recipient]
46
- end
47
-
48
- if messages.first.nil?
49
- log.error "#{self} was not found (recipient: '#{recipient}')"
50
- return # TODO check log.error raises error
51
- end
52
- new(messages.first)
53
- end
54
-
55
- ##
56
- #
57
- # Returns plain text body of email message
58
- #
59
-
60
- def plain_text_body
61
- get_mime_part(@message, 'text/plain').to_s
62
- end
63
-
64
- ##
65
- #
66
- # Allows to get email MIME attachment
67
- #
68
- # *Parameters:*
69
- # * +part+ - recepient's email address
70
- # * +type+ - MIME message part
71
- #
72
-
73
- def get_mime_part(part, type)
74
- return part.body if part["content-type"].to_s =~ %r!#{type}!
75
- # Recurse the multi-parts
76
- part.parts.each do |sub_part|
77
- r = get_mime_part(sub_part, type)
78
- return r if r
79
- end
80
- nil
81
- end
82
-
83
- protected :get_mime_part
84
-
85
- end
@@ -1,132 +0,0 @@
1
- require 'net/pop'
2
- require 'timeout'
3
- require 'mail'
4
- require 'howitzer/utils/email/mailgun_helper'
5
-
6
- class MailClient
7
-
8
- extend MailgunHelper
9
- @clients = {}
10
-
11
- def self.default
12
- log.info "Connect to default mailbox"
13
- options = merge_opts
14
- @clients[options] = new unless @clients.has_key?(options)
15
- @clients[options]
16
- end
17
-
18
- def self.by_email(name)
19
- log.info "Connect to '#{name}' mailbox"
20
- options = self.merge_opts(pop3: { user_name: name }, smtp: {})
21
- @clients[options] = new(options) unless @clients.has_key?(options)
22
- @clients[options]
23
- end
24
-
25
- def self.merge_opts(opts={smtp: {}, pop3: {}})
26
- def_smtp_opts = {
27
- address: settings.mail_smtp_server,
28
- port: settings.mail_smtp_port,
29
- domain: settings.mail_smtp_domain,
30
- user_name: settings.mail_smtp_user_name,
31
- password: settings.mail_smtp_user_pass,
32
- authentication: 'plain',
33
- enable_starttls_auto: true
34
- }
35
-
36
- def_pop3_opts = {
37
- address: settings.mail_pop3_server,
38
- port: settings.mail_pop3_port,
39
- user_name: settings.mail_pop3_user_name,
40
- password: settings.mail_pop3_user_pass
41
- }
42
- {
43
- smtp: def_smtp_opts.merge(opts[:smtp]),
44
- pop3: def_pop3_opts.merge(opts[:pop3])
45
- }
46
- end
47
-
48
- def find_mail(max_wait = settings.mail_pop3_timeout, keep_or_delete = :delete, &block)
49
- messages = []
50
- time_of_start = Time.now
51
- exception = nil
52
- while Time.now < time_of_start + max_wait
53
- begin
54
- exception = nil
55
- start do |pop_obj|
56
- pop_obj.mails.each do |mail|
57
- begin
58
- mail_message = Mail.new(mail.pop.to_s)
59
- if block_given?
60
- if block.call(mail_message)
61
- if keep_or_delete == :delete
62
- mail.delete
63
- log.info "Mail '#{mail_message.subject}' deleted from #{@options[:pop3][:user_name]}"
64
- end
65
- messages << mail_message
66
- @old_ids << mail.unique_id
67
- return messages
68
- end
69
- else
70
- messages << mail_message
71
- end
72
- rescue Net::POPError => e
73
- log.warn "Exception in POP find_mail: #{e.message}"
74
- exception = e
75
- end
76
- end
77
- end
78
-
79
- sleep 5
80
- rescue Errno::ETIMEDOUT, Errno::ECONNRESET, Net::POPAuthenticationError => e
81
- log.warn "Exception in POP find_mail: #{e.message}. Will retry."
82
- exception = e
83
- end
84
- end
85
- log.error exception unless exception.nil?
86
- messages
87
- end
88
-
89
- def send_mail(mail_from, mail_to, mail_subject, mail_body, mail_attachment=nil)
90
- log.info "Send emails with next parameters:\n " +
91
- "mail_from : #{mail_from},\n mail_to : #{mail_to}\n " +
92
- "mail_subject : #{mail_subject},\n has_attachment? : #{!mail_attachment.nil?}"
93
- outbound_mail = Mail.new do
94
- from(mail_from)
95
- subject(mail_subject)
96
- body(mail_body)
97
- add_file(mail_attachment) unless mail_attachment.nil?
98
- end
99
- outbound_mail[:to] = mail_to
100
-
101
- retryable(:on => [Errno::ETIMEDOUT, Timeout::Error], :sleep => 10, :tries => 3, :trace => true, :logger => log) do
102
- outbound_mail.deliver!
103
- end
104
- end
105
-
106
- def empty_inbox
107
- begin
108
- start {|pop_obj| pop_obj.delete_all }
109
- log.info "Email box (#{@options[:pop3][:user_name]}) was purged"
110
- rescue Net::POPError => e
111
- log.warn "Exception during deletion all messages from '#{@options[:pop3][:user_name]}':\n#{e.message}"
112
- end
113
- end
114
-
115
- def start(&block)
116
- retryable(:timeout => settings.mail_pop3_timeout, :sleep => 5, :trace => true, :logger => log) do
117
- Net::POP3.start(@options[:pop3][:address], @options[:pop3][:port],
118
- @options[:pop3][:user_name], @options[:pop3][:password], &block)
119
- end
120
- end
121
-
122
- private
123
- def initialize(*arg)
124
- @options = self.class.merge_opts(*arg)
125
- Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
126
- @old_ids = []
127
- options = @options
128
- ::Mail.defaults{delivery_method :smtp, options[:smtp]}
129
- @time_of_start = Time.now
130
- end
131
-
132
- end