howitzer 0.0.3 → 1.0.1

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. data/.gitignore +8 -0
  2. data/.rspec +3 -0
  3. data/.travis.yml +4 -0
  4. data/CHANGELOG.md +32 -0
  5. data/GETTING_STARTED.md +529 -0
  6. data/Gemfile +4 -2
  7. data/LICENSE +2 -2
  8. data/README.md +57 -13
  9. data/Rakefile +9 -2
  10. data/bin/howitzer +79 -31
  11. data/generators/base_generator.rb +87 -0
  12. data/generators/config/config_generator.rb +16 -20
  13. data/generators/config/templates/default.yml +26 -7
  14. data/generators/cucumber/cucumber_generator.rb +20 -26
  15. data/generators/{tasks → cucumber}/templates/cucumber.rake +0 -0
  16. data/generators/{config → cucumber}/templates/cucumber.yml +0 -0
  17. data/generators/emails/emails_generator.rb +11 -18
  18. data/generators/emails/templates/example_email.rb +1 -0
  19. data/generators/pages/pages_generator.rb +16 -18
  20. data/generators/pages/templates/example_menu.rb +1 -0
  21. data/generators/pages/templates/example_page.rb +1 -1
  22. data/generators/root/root_generator.rb +18 -20
  23. data/generators/root/templates/.gitignore +2 -1
  24. data/generators/root/templates/Gemfile +3 -2
  25. data/generators/root/templates/Rakefile +10 -0
  26. data/generators/root/templates/boot.rb +3 -9
  27. data/generators/rspec/rspec_generator.rb +23 -0
  28. data/generators/rspec/templates/example_spec.rb +7 -0
  29. data/generators/rspec/templates/rspec.rake +34 -0
  30. data/generators/rspec/templates/spec_helper.rb +56 -0
  31. data/generators/tasks/tasks_generator.rb +11 -17
  32. data/generators/tasks/templates/common.rake +15 -0
  33. data/howitzer.gemspec +13 -2
  34. data/lib/howitzer.rb +1 -0
  35. data/lib/howitzer/helpers.rb +87 -2
  36. data/lib/howitzer/init.rb +0 -1
  37. data/lib/howitzer/patches/rawler_patched.rb +86 -0
  38. data/lib/howitzer/settings.rb +27 -0
  39. data/lib/howitzer/utils.rb +3 -12
  40. data/lib/howitzer/utils/capybara_patched.rb +3 -2
  41. data/lib/howitzer/utils/capybara_settings.rb +158 -24
  42. data/lib/howitzer/utils/data_generator/data_storage.rb +35 -1
  43. data/lib/howitzer/utils/data_generator/gen.rb +45 -3
  44. data/lib/howitzer/utils/email/email.rb +44 -5
  45. data/lib/howitzer/utils/email/mail_client.rb +28 -22
  46. data/lib/howitzer/utils/email/mailgun_helper.rb +30 -4
  47. data/lib/howitzer/utils/locator_store.rb +111 -19
  48. data/lib/howitzer/utils/log.rb +137 -0
  49. data/lib/howitzer/utils/page_validator.rb +86 -0
  50. data/lib/howitzer/vendor/firebug-1.12.1-fx.xpi +0 -0
  51. data/lib/howitzer/vendor/firepath-0.9.7-fx.xpi +0 -0
  52. data/lib/howitzer/version.rb +2 -2
  53. data/lib/howitzer/web_page.rb +159 -19
  54. data/spec/active_resource.rb +0 -0
  55. data/spec/config/custom.yml +1 -0
  56. data/spec/config/default.yml +28 -0
  57. data/spec/spec_helper.rb +46 -1
  58. data/spec/support/boot_helper.rb +15 -0
  59. data/spec/support/generator_helper.rb +13 -0
  60. data/spec/support/logger_helper.rb +12 -0
  61. data/spec/unit/bin/howitzer_spec.rb +175 -0
  62. data/spec/unit/generators/generators_spec.rb +175 -0
  63. data/spec/unit/lib/capybara_settings_spec.rb +170 -0
  64. data/spec/unit/lib/helpers_spec.rb +619 -0
  65. data/spec/unit/lib/init_spec.rb +14 -0
  66. data/spec/unit/lib/settings_spec.rb +17 -0
  67. data/spec/unit/lib/utils/data_generator/data_storage_spec.rb +62 -0
  68. data/spec/unit/lib/utils/data_generator/gen_spec.rb +151 -0
  69. data/spec/unit/lib/utils/email/email_spec.rb +75 -0
  70. data/spec/unit/lib/utils/email/mail_client_spec.rb +115 -0
  71. data/spec/unit/lib/utils/email/mailgun_helper_spec.rb +95 -0
  72. data/spec/unit/lib/utils/locator_store_spec.rb +122 -0
  73. data/spec/unit/lib/utils/log_spec.rb +107 -0
  74. data/spec/unit/lib/utils/page_validator_spec.rb +142 -0
  75. data/spec/unit/lib/web_page_spec.rb +250 -0
  76. data/spec/unit/version_spec.rb +8 -0
  77. metadata +215 -15
  78. data/Gemfile.lock +0 -103
  79. data/History.md +0 -20
  80. data/lib/howitzer/utils/logger.rb +0 -108
  81. data/spec/howitzer/version_spec.rb +0 -8
@@ -1,19 +1,53 @@
1
1
  module DataGenerator
2
2
 
3
+ ##
4
+ #
5
+ # Data can be stored in memory using DataStorage
6
+ #
7
+
3
8
  module DataStorage
4
9
  @data ||= {}
5
10
 
6
11
  class << self
12
+
13
+ ##
14
+ #
15
+ # Saves data into memory. Marking by namespace and key
16
+ #
17
+ # *Parameters:*
18
+ # * +ns+ - Namespace
19
+ # * +key+ - Key that should be uniq in namespace
20
+ # * +value+ - Data value
21
+ #
22
+
23
+
7
24
  def store(ns, key, value)
8
25
  check_ns(ns)
9
26
  @data[ns][key] = value
10
27
  end
11
28
 
29
+ ##
30
+ #
31
+ # Gets data from memory. Can get all namespace or single data value in namespace using key
32
+ #
33
+ # *Parameters:*
34
+ # * +ns+ - Namespace
35
+ # * +key+ - Key that isn't necessary required (default to: nil)
36
+ #
37
+
12
38
  def extract(ns, key=nil)
13
39
  check_ns(ns)
14
40
  key ? @data[ns][key] : @data[ns]
15
41
  end
16
42
 
43
+ ##
44
+ #
45
+ # Deletes all records from namespace
46
+ #
47
+ # *Parameters:*
48
+ # * +ns+ - Namespace
49
+ #
50
+
17
51
  def clear_ns(ns)
18
52
  init_ns(ns)
19
53
  end
@@ -38,4 +72,4 @@ module DataGenerator
38
72
 
39
73
  end
40
74
  end
41
- end
75
+ end
@@ -1,11 +1,28 @@
1
1
  module DataGenerator
2
2
  module Gen
3
- # examples:
3
+
4
+ ##
5
+ #
6
+ # This module generates uniq User object with uniq generated data.
7
+ # Examples:
4
8
  # member = Gen::user #user.email: 'u<XXXX>@<settings.mail_pop3_domain>'
5
9
  # member = Gen::user('user@test.com') #user.email: 'member@test.com'
6
- # member = Gen::user(settings.def_test_user) #user.email: settings.def_test_user
10
+ # member = Gen::user(settings.def_test_user) #user.email: settings.def_test_user
7
11
 
8
12
  class << self
13
+
14
+ ##
15
+ #
16
+ # Generates new User object with generated data.
17
+ #
18
+ # *Parameters:*
19
+ # * +params+ - Custom parameters
20
+ #
21
+ # *Returns:*
22
+ # * +user+ - New generated User object
23
+ #
24
+
25
+
9
26
  def user(params={})
10
27
  prefix = serial
11
28
  default = {
@@ -18,6 +35,17 @@ module DataGenerator
18
35
  User.new(default.merge(params))
19
36
  end
20
37
 
38
+ ##
39
+ #
40
+ # Gets User object by it's number
41
+ #
42
+ # *Parameters:*
43
+ # * +num+ - User number
44
+ #
45
+ # *Returns:*
46
+ # * +user+ - User object
47
+ #
48
+
21
49
  def given_user_by_number(num)
22
50
  data = DataStorage.extract('user', num.to_i)
23
51
  unless data
@@ -27,11 +55,25 @@ module DataGenerator
27
55
  data
28
56
  end
29
57
 
58
+ ##
59
+ #
60
+ # Generates uniq string
61
+ # @return [String] Generated string
62
+ #
63
+ # *Returns:*
64
+ # * +string+ - Generated string
65
+ #
66
+
30
67
  def serial
31
68
  a = [('a'..'z').to_a, (0..9).to_a].flatten.shuffle
32
69
  "#{Time.now.utc.strftime("%j%H%M%S")}#{a[0..4].join}"
33
70
  end
34
71
 
72
+ ##
73
+ #
74
+ # Deletes mailboxes for all users that were generated before
75
+ #
76
+
35
77
  def delete_all_mailboxes
36
78
  DataStorage.extract('user').each_value do |user|
37
79
  user.delete_mailbox
@@ -86,4 +128,4 @@ module DataGenerator
86
128
  end
87
129
  end
88
130
  end
89
- end
131
+ end
@@ -5,32 +5,71 @@ class Email
5
5
  include RSpec::Matchers
6
6
  attr_reader :recipient_address
7
7
 
8
+ ##
9
+ #
10
+ # Creates new email with message
11
+ #
12
+ # *Parameters:*
13
+ # * +message+ - email message
14
+ #
15
+
8
16
  def initialize(message)
9
- message.subject.should include(self.class::SUBJECT)
17
+ expect(message.subject).to include(self.class::SUBJECT)
10
18
  @recipient_address = ::Mail::Address.new(message.to.first)
11
19
  @message = message
12
20
  end
13
21
 
22
+ ##
23
+ #
24
+ # Search mail by recepient
25
+ #
26
+ # *Parameters:*
27
+ # * +recepient+ - recepient's email address
28
+ #
29
+
14
30
  def self.find_by_recipient(recipient)
15
31
  find(recipient, self::SUBJECT)
16
32
  end
17
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
+
18
43
  def self.find(recipient, subject)
19
- messages = MailClient.by_email(recipient).find_mail do |mail|
20
- /#{Regexp.escape(subject)}/ === mail.subject && mail.to == [recipient]
44
+ messages = MailClient.by_email(recipient).find_mail do |mail|
45
+ /#{Regexp.escape(subject)}/ === mail.subject && mail.to == [recipient]
21
46
  end
22
47
 
23
48
  if messages.first.nil?
24
49
  log.error "#{self} was not found (recipient: '#{recipient}')"
25
- messages.first.should_not be_nil
50
+ return # TODO check log.error raises error
26
51
  end
27
52
  new(messages.first)
28
53
  end
29
54
 
55
+ ##
56
+ #
57
+ # Returns plain text body of email message
58
+ #
59
+
30
60
  def plain_text_body
31
61
  get_mime_part(@message, 'text/plain').to_s
32
62
  end
33
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
+
34
73
  def get_mime_part(part, type)
35
74
  return part.body if part["content-type"].to_s =~ %r!#{type}!
36
75
  # Recurse the multi-parts
@@ -43,4 +82,4 @@ class Email
43
82
 
44
83
  protected :get_mime_part
45
84
 
46
- end
85
+ end
@@ -10,18 +10,41 @@ class MailClient
10
10
 
11
11
  def self.default
12
12
  log.info "Connect to default mailbox"
13
- options = self.merge_opts
14
- @clients[options] = MailClient.send :new unless @clients.has_key?(options)
13
+ options = merge_opts
14
+ @clients[options] = new unless @clients.has_key?(options)
15
15
  @clients[options]
16
16
  end
17
17
 
18
18
  def self.by_email(name)
19
19
  log.info "Connect to '#{name}' mailbox"
20
- options = self.merge_opts(:pop3 => {:user_name => name}, :smtp => {})
21
- @clients[options] = MailClient.send :new, options unless @clients.has_key?(options)
20
+ options = self.merge_opts(pop3: { user_name: name }, smtp: {})
21
+ @clients[options] = new(options) unless @clients.has_key?(options)
22
22
  @clients[options]
23
23
  end
24
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
+
25
48
  def find_mail(max_wait = settings.mail_pop3_timeout, keep_or_delete = :delete, &block)
26
49
  messages = []
27
50
  time_of_start = Time.now
@@ -98,7 +121,7 @@ class MailClient
98
121
 
99
122
  private
100
123
  def initialize(*arg)
101
- @options = MailClient.merge_opts(*arg)
124
+ @options = self.class.merge_opts(*arg)
102
125
  Net::POP3.enable_ssl(OpenSSL::SSL::VERIFY_NONE)
103
126
  @old_ids = []
104
127
  options = @options
@@ -106,21 +129,4 @@ class MailClient
106
129
  @time_of_start = Time.now
107
130
  end
108
131
 
109
- def self.merge_opts(opts={:smtp => {}, :pop3 => {}})
110
- def_smtp_opts = {:address => settings.mail_smtp_server,
111
- :port => settings.mail_smtp_port,
112
- :domain => settings.mail_smtp_domain,
113
- :user_name => settings.mail_smtp_user_name,
114
- :password => settings.mail_smtp_user_pass,
115
- :authentication => 'plain',
116
- :enable_starttls_auto => true}
117
-
118
- def_pop3_opts = {:address => settings.mail_pop3_server,
119
- :port => settings.mail_pop3_port,
120
- :user_name => settings.mail_pop3_user_name,
121
- :password => settings.mail_pop3_user_pass}
122
- {:smtp => def_smtp_opts.merge(opts[:smtp]),
123
- :pop3 => def_pop3_opts.merge(opts[:pop3])}
124
- end
125
-
126
132
  end
@@ -1,5 +1,16 @@
1
1
  module MailgunHelper
2
2
 
3
+ ##
4
+ #
5
+ # Creates new mailbox for given user_name
6
+ #
7
+ # *Parameters:*
8
+ # * +user_name+ - user name
9
+ # * +domain+ - domain (default to: settings.mail_pop3_domain )
10
+ # * +password+ - password (default to: settings.mail_pop3_user_pass )
11
+ #
12
+
13
+
3
14
  def create_mailbox(user_name,
4
15
  domain=settings.mail_pop3_domain,
5
16
  password=settings.mail_pop3_user_pass)
@@ -9,6 +20,14 @@ module MailgunHelper
9
20
  mbox
10
21
  end
11
22
 
23
+ ##
24
+ #
25
+ # Deletes given mailbox
26
+ #
27
+ # *Parameters:*
28
+ # * +mailbox+ - mailbox to delete
29
+ #
30
+
12
31
  def delete_mailbox(mailbox)
13
32
  log.info "Delete '#{mailbox.user}@#{mailbox.domain}' mailbox"
14
33
  begin
@@ -18,11 +37,18 @@ module MailgunHelper
18
37
  end
19
38
  end
20
39
 
40
+ ##
41
+ #
42
+ # Deletes all mailboxes except those in exceptions
43
+ #
44
+ # *Parameters:*
45
+ # * +exceptions+ - list of email addresses
46
+ #
47
+
21
48
  def delete_all_mailboxes(*exceptions)
22
- puts exceptions.inspect
23
- exceptions += %w"postmaster@#{settings.mail_smtp_domain}" #system and default mailbox
49
+ exceptions += ["postmaster@#{settings.mail_smtp_domain}"] #system and default mailbox
24
50
  exceptions = exceptions.uniq
25
- log.info "Delete all mailboxes #{"except: " + exceptions.inspect unless exceptions.empty?}"
51
+ log.info "Delete all mailboxes except: #{exceptions.map(&:inspect).join(', ')}"
26
52
  i = 0
27
53
  Mailbox.find(:all).each do |m|
28
54
  next if exceptions.include?("#{m.user}@#{m.domain}")
@@ -32,4 +58,4 @@ module MailgunHelper
32
58
 
33
59
  log.info "Were deleted '#{i}' mailboxes"
34
60
  end
35
- end
61
+ end
@@ -18,52 +18,156 @@
18
18
 
19
19
 
20
20
  module LocatorStore
21
+ BadLocatorParamsError = Class.new(StandardError)
22
+ LocatorNotDefinedError = Class.new(StandardError)
23
+
21
24
  def self.included(base)
22
25
  base.extend(ClassMethods)
23
26
  end
24
27
 
25
28
  module ClassMethods
29
+ LOCATOR_TYPES = [:base, :link, :field, :button]
26
30
  class BadLocatorParamsError < StandardError; end
27
31
  class LocatorNotSpecifiedError < StandardError; end
28
32
 
33
+ ##
34
+ #
35
+ # Adds css or xpath locator into LocatorStore.
36
+ # Also locator can be set by lambda expression. For example: lambda{|name|{xpath: ".//a[@id='#{name}']"}}
37
+ #
38
+ # *Parameters:*
39
+ # * +name+ - Locator name
40
+ # * +params+ - String for css locator or hash with :xpath key and string value for xpath locator
41
+ #
42
+
29
43
  def add_locator(name, params)
30
44
  add_locator_by_type(:base, name, params)
31
45
  end
32
46
 
47
+ ##
48
+ #
49
+ # Adds locator for link into LocatorStore. Link can be found by: id, text
50
+ # Capybara methods that can work with this locator type are: click_link, find_link
51
+ #
52
+ # *Parameters:*
53
+ # * +name+ - Locator name
54
+ # * +params+ - ID or text of link
55
+ #
56
+
33
57
  def add_link_locator(name, params)
34
58
  add_locator_by_type(:link, name, params)
35
59
  end
36
60
 
61
+ ##
62
+ #
63
+ # Adds locator for field into LocatorStore. Field can be found by: name, id, text
64
+ # Capybara methods that can work with this locator type are: find_field, fill_in
65
+ #
66
+ # *Parameters:*
67
+ # * +name+ - Locator name
68
+ # * +params+ - Name, ID or text of field
69
+ #
70
+
37
71
  def add_field_locator(name, params)
38
72
  add_locator_by_type(:field, name, params)
39
73
  end
40
74
 
75
+ ##
76
+ #
77
+ # Add locator for button into LocatorStore. Button can be found by: id, name, value
78
+ # Capybara methods that can work with this locator type are: click_button, find_button
79
+ #
80
+ # *Parameters:*
81
+ # * +name+ - Locator name
82
+ # * +params+ - Name, ID or value
83
+ #
84
+
41
85
  def add_button_locator(name, params)
42
86
  add_locator_by_type(:button, name, params)
43
87
  end
44
88
 
89
+ ##
90
+ #
91
+ # Takes css or xpath locator from LocatorStore by name
92
+ #
93
+ # *Parameters:*
94
+ # * +name+ - Locator name
95
+ #
96
+
45
97
  def locator(name)
46
98
  locator_by_type(:base, name)
47
99
  end
48
100
 
101
+ ##
102
+ #
103
+ # Take link locator from LocatorStore by name
104
+ #
105
+ # *Parameters:*
106
+ # * +name+ - Locator name
107
+ #
108
+
49
109
  def link_locator(name)
50
110
  locator_by_type(:link, name)
51
111
  end
52
112
 
113
+ ##
114
+ #
115
+ # Take field locator from LocatorStore by name
116
+ #
117
+ # *Parameters:*
118
+ # * +name+ - Locator name
119
+ #
120
+
53
121
  def field_locator(name)
54
122
  locator_by_type(:field, name)
55
123
  end
56
124
 
125
+ ##
126
+ #
127
+ # Take button locator from LocatorStore be name
128
+ #
129
+ # *Parameters:*
130
+ # * +name+ - Locator name
131
+ #
132
+
57
133
  def button_locator(name)
58
134
  locator_by_type(:button, name)
59
135
  end
60
136
 
137
+ ##
138
+ #
139
+ # Get locator set by lambda.
140
+ # For example: find(apply(locator(:locator_name), 'register')).click
141
+ #
142
+ # *Parameters:*
143
+ # * +locator+ - Locator set with lambda expression
144
+ # * +values+ - Arguments that should be matched lambda expression params
145
+ #
146
+
61
147
  def apply(locator, *values)
62
148
  locator.call(*values).to_a.flatten
63
149
  end
64
150
 
151
+ def find_element(name)
152
+ type, locator = find_locator(name)
153
+ if type == :base
154
+ send :find, locator
155
+ else
156
+ send "find_#{type}", locator
157
+ end
158
+ end
159
+
65
160
  protected
66
161
 
162
+ def find_locator(name)
163
+ name = name.to_s.to_sym
164
+ LOCATOR_TYPES.each do|type|
165
+ return [type, locator_by_type(type, name)] if (@locators || {}).fetch(self.name, {}).fetch(type, {})[name]
166
+ end
167
+ raise(LocatorNotDefinedError, name)
168
+ end
169
+
170
+ # looks up locator in current and all super classes
67
171
  def parent_locator(type, name)
68
172
  if !@locators.nil? && @locators.key?(self.name) && @locators[self.name].key?(type) && @locators[self.name][type].key?(name)
69
173
  @locators[self.name][type][name]
@@ -76,7 +180,7 @@ module LocatorStore
76
180
 
77
181
  def locator_by_type(type, name)
78
182
  locator = parent_locator(type, name)
79
- raise(LocatorNotSpecifiedError, name) if locator.nil?
183
+ raise(LocatorNotDefinedError, name) if locator.nil?
80
184
  locator
81
185
  end
82
186
 
@@ -96,23 +200,11 @@ module LocatorStore
96
200
  end
97
201
  end
98
202
 
99
- def locator(name)
100
- self.class.locator(name)
101
- end
102
-
103
- def link_locator(name)
104
- self.class.link_locator(name)
105
- end
106
-
107
- def field_locator(name)
108
- self.class.field_locator(name)
109
- end
110
-
111
- def button_locator(name)
112
- self.class.button_locator(name)
203
+ #delegate class methods to instance
204
+ ClassMethods.public_instance_methods.each do |name|
205
+ define_method(name) do |*args|
206
+ self.class.send(name, *args)
207
+ end
113
208
  end
114
209
 
115
- def apply(locator, *values)
116
- self.class.apply(locator, *values)
117
- end
118
- end
210
+ end