howitzer 0.0.3 → 1.0.1

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