howitzer 1.0.1 → 1.0.2

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