howitzer 1.0.2 → 1.1.0
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.
- checksums.yaml +4 -4
- data/.coveralls.yml +1 -0
- data/.gitignore +3 -1
- data/.ruby-gemset +1 -0
- data/.travis.yml +4 -1
- data/.yardopts +5 -0
- data/CHANGELOG.md +23 -1
- data/CONTRIBUTING.md +14 -0
- data/GETTING_STARTED.md +283 -183
- data/Gemfile +3 -2
- data/LICENSE +1 -1
- data/README.md +93 -39
- data/Rakefile +4 -0
- data/bin/howitzer +34 -5
- data/features/cli_help.feature +3 -2
- data/features/cli_new.feature +1 -1
- data/features/cli_unknown.feature +1 -1
- data/features/cli_update.feature +84 -0
- data/features/step_definitions/common_steps.rb +9 -1
- data/generators/base_generator.rb +30 -15
- data/generators/config/config_generator.rb +7 -7
- data/generators/config/templates/custom.yml +1 -0
- data/generators/config/templates/default.yml +35 -5
- data/generators/cucumber/templates/env.rb +2 -2
- data/generators/cucumber/templates/transformers.rb +3 -1
- data/generators/root/templates/Gemfile +5 -3
- data/generators/root/templates/Rakefile +2 -0
- data/generators/rspec/templates/example_spec.rb +3 -3
- data/generators/rspec/templates/spec_helper.rb +6 -7
- data/howitzer.gemspec +15 -15
- data/lib/howitzer/capybara/settings.rb +125 -49
- data/lib/howitzer/helpers.rb +161 -94
- data/lib/howitzer/mailgun/client.rb +1 -1
- data/lib/howitzer/tasks/framework.rake +3 -0
- data/lib/howitzer/utils.rb +1 -1
- data/lib/howitzer/utils/locator_store.rb +1 -1
- data/lib/howitzer/utils/log.rb +1 -1
- data/lib/howitzer/utils/page_validator.rb +1 -1
- data/lib/howitzer/version.rb +1 -1
- data/lib/howitzer/web_page.rb +11 -11
- data/spec/spec_helper.rb +25 -22
- data/spec/support/generator_helper.rb +8 -1
- data/spec/unit/generators/base_generator_spec.rb +242 -0
- data/spec/unit/generators/config_generator_spec.rb +34 -0
- data/spec/unit/generators/cucumber_generator_spec.rb +45 -0
- data/spec/unit/generators/emails_generator_spec.rb +31 -0
- data/spec/unit/generators/pages_generator_spec.rb +33 -0
- data/spec/unit/generators/root_generator_spec.rb +35 -0
- data/spec/unit/generators/rspec_generator_spec.rb +36 -0
- data/spec/unit/generators/tasks_generator_spec.rb +31 -0
- data/spec/unit/lib/capybara/dsl_ex_spec.rb +11 -11
- data/spec/unit/lib/capybara/settings_spec.rb +336 -58
- data/spec/unit/lib/email_spec.rb +17 -17
- data/spec/unit/lib/helpers_spec.rb +699 -315
- data/spec/unit/lib/mailgun/client_spec.rb +9 -9
- data/spec/unit/lib/mailgun/connector_spec.rb +20 -20
- data/spec/unit/lib/mailgun/response_spec.rb +9 -9
- data/spec/unit/lib/settings_spec.rb +6 -6
- data/spec/unit/lib/utils/data_generator/data_storage_spec.rb +31 -31
- data/spec/unit/lib/utils/data_generator/gen_spec.rb +20 -20
- data/spec/unit/lib/utils/locator_store_spec.rb +39 -39
- data/spec/unit/lib/utils/log_spec.rb +42 -42
- data/spec/unit/lib/utils/page_validator_spec.rb +69 -70
- data/spec/unit/lib/web_page_spec.rb +91 -69
- data/spec/unit/version_spec.rb +3 -3
- metadata +100 -78
- data/spec/unit/generators/generators_spec.rb +0 -175
data/lib/howitzer/helpers.rb
CHANGED
@@ -1,121 +1,178 @@
|
|
1
1
|
require 'howitzer/exceptions'
|
2
2
|
|
3
|
-
|
3
|
+
module Helpers
|
4
4
|
|
5
|
-
|
6
|
-
log.error Howitzer::DriverNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.driver.nil?
|
7
|
-
settings.driver.to_sym == :sauce
|
8
|
-
end
|
5
|
+
CHECK_YOUR_SETTINGS_MSG = "Please check your settings"
|
9
6
|
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
7
|
+
##
|
8
|
+
#
|
9
|
+
# Returns whether or not the current driver is SauceLabs.
|
10
|
+
#
|
14
11
|
|
15
|
-
def
|
16
|
-
|
17
|
-
|
18
|
-
end
|
12
|
+
def sauce_driver?
|
13
|
+
log.error Howitzer::DriverNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.driver.nil?
|
14
|
+
settings.driver.to_s.to_sym == :sauce
|
15
|
+
end
|
19
16
|
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
17
|
+
##
|
18
|
+
#
|
19
|
+
# Returns whether or not the current driver is TestingBot.
|
20
|
+
#
|
24
21
|
|
25
|
-
def
|
26
|
-
|
27
|
-
|
28
|
-
log.error Howitzer::SlBrowserNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.sl_browser_name.nil?
|
29
|
-
ie_browsers.include?(settings.sl_browser_name.to_sym)
|
30
|
-
elsif testingbot_driver?
|
31
|
-
log.error Howitzer::TbBrowserNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.tb_browser_name.nil?
|
32
|
-
ie_browsers.include?(settings.tb_browser_name.to_sym)
|
33
|
-
elsif selenium_driver?
|
34
|
-
log.error Howitzer::SelBrowserNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.sel_browser.nil?
|
35
|
-
ie_browsers.include?(settings.sel_browser.to_sym)
|
22
|
+
def testingbot_driver?
|
23
|
+
log.error Howitzer::DriverNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.driver.nil?
|
24
|
+
settings.driver.to_s.to_sym == :testingbot
|
36
25
|
end
|
37
|
-
end
|
38
26
|
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
log.error Howitzer::
|
46
|
-
|
47
|
-
elsif selenium_driver?
|
48
|
-
log.error Howitzer::SelBrowserNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.sel_browser.nil?
|
49
|
-
ff_browsers.include?(settings.sel_browser.to_sym)
|
27
|
+
##
|
28
|
+
#
|
29
|
+
# Returns whether or not the current driver is Selenium.
|
30
|
+
#
|
31
|
+
|
32
|
+
def selenium_driver?
|
33
|
+
log.error Howitzer::DriverNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.driver.nil?
|
34
|
+
settings.driver.to_s.to_sym == :selenium
|
50
35
|
end
|
51
|
-
end
|
52
36
|
|
37
|
+
##
|
38
|
+
#
|
39
|
+
# Returns whether or not the current driver is Selenium Grid.
|
40
|
+
#
|
53
41
|
|
54
|
-
def
|
55
|
-
|
56
|
-
|
57
|
-
log.error Howitzer::SlBrowserNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.sl_browser_name.nil?
|
58
|
-
settings.sl_browser_name.to_sym == chrome_browser
|
59
|
-
elsif testingbot_driver?
|
60
|
-
log.error Howitzer::TbBrowserNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.tb_browser_name.nil?
|
61
|
-
settings.tb_browser_name.to_sym == chrome_browser
|
62
|
-
elsif selenium_driver?
|
63
|
-
log.error Howitzer::SelBrowserNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.sel_browser.nil?
|
64
|
-
settings.sel_browser.to_sym == chrome_browser
|
42
|
+
def selenium_grid_driver?
|
43
|
+
log.error Howitzer::DriverNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.driver.nil?
|
44
|
+
settings.driver.to_s.to_sym == :selenium_grid
|
65
45
|
end
|
66
|
-
end
|
67
46
|
|
47
|
+
##
|
48
|
+
#
|
49
|
+
# Returns whether or not the current driver is PhantomJS.
|
50
|
+
#
|
51
|
+
|
52
|
+
def phantomjs_driver?
|
53
|
+
log.error Howitzer::DriverNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.driver.nil?
|
54
|
+
settings.driver.to_s.to_sym == :phantomjs
|
55
|
+
end
|
68
56
|
|
69
|
-
##
|
70
|
-
#
|
71
|
-
# Returns
|
72
|
-
#
|
57
|
+
##
|
58
|
+
#
|
59
|
+
# Returns whether or not the current browser is Internet Explorer.
|
60
|
+
#
|
73
61
|
|
74
|
-
def
|
75
|
-
|
76
|
-
|
77
|
-
end
|
62
|
+
def ie_browser?
|
63
|
+
browser? :ie, :iexplore
|
64
|
+
end
|
78
65
|
|
79
|
-
##
|
80
|
-
#
|
81
|
-
#
|
82
|
-
#
|
83
|
-
# * +prefix+ - Sets base authentication prefix (defaults to: nil)
|
84
|
-
#
|
66
|
+
##
|
67
|
+
#
|
68
|
+
# Returns whether or not the current browser is FireFox.
|
69
|
+
#
|
85
70
|
|
86
|
-
def
|
87
|
-
|
88
|
-
end
|
71
|
+
def ff_browser?
|
72
|
+
browser? :ff, :firefox
|
73
|
+
end
|
89
74
|
|
90
|
-
|
91
|
-
#
|
92
|
-
#
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
hours = mins / 60
|
98
|
-
if hours > 0
|
99
|
-
"[#{hours}h #{mins % 60}m #{secs % 60}s]"
|
100
|
-
elsif mins > 0
|
101
|
-
"[#{mins}m #{secs % 60}s]"
|
102
|
-
elsif secs >= 0
|
103
|
-
"[0m #{secs}s]"
|
75
|
+
##
|
76
|
+
#
|
77
|
+
# Returns whether or not the current browser is Google Chrome.
|
78
|
+
#
|
79
|
+
|
80
|
+
def chrome_browser?
|
81
|
+
browser? :chrome
|
104
82
|
end
|
105
|
-
end
|
106
83
|
|
107
|
-
##
|
108
|
-
#
|
109
|
-
#
|
110
|
-
#
|
111
|
-
|
112
|
-
|
113
|
-
|
84
|
+
##
|
85
|
+
#
|
86
|
+
# Returns whether or not the current browser is Opera.
|
87
|
+
#
|
88
|
+
|
89
|
+
def opera_browser?
|
90
|
+
browser? :opera
|
91
|
+
end
|
92
|
+
|
93
|
+
##
|
94
|
+
#
|
95
|
+
# Returns whether or not the current browser is Safari.
|
96
|
+
#
|
97
|
+
|
98
|
+
def safari_browser?
|
99
|
+
browser? :safari
|
100
|
+
end
|
101
|
+
|
102
|
+
##
|
103
|
+
#
|
104
|
+
# Returns application url including base authentication (if specified in settings)
|
105
|
+
#
|
106
|
+
|
107
|
+
def app_url
|
108
|
+
prefix = settings.app_base_auth_login.blank? ? '' : "#{settings.app_base_auth_login}:#{settings.app_base_auth_pass}@"
|
109
|
+
app_base_url prefix
|
110
|
+
end
|
111
|
+
|
112
|
+
##
|
113
|
+
#
|
114
|
+
# Returns application url without base authentication by default
|
115
|
+
#
|
116
|
+
# *Parameters:*
|
117
|
+
# * +prefix+ - Sets base authentication prefix (defaults to: nil)
|
118
|
+
#
|
119
|
+
|
120
|
+
def app_base_url(prefix=nil)
|
121
|
+
"#{settings.app_protocol || 'http'}://#{prefix}#{settings.app_host}"
|
122
|
+
end
|
123
|
+
|
124
|
+
##
|
125
|
+
#
|
126
|
+
# Returns formatted duration time
|
127
|
+
#
|
128
|
+
# *Parameters:*
|
129
|
+
# * +time_in_numeric+ - Number of seconds
|
130
|
+
#
|
131
|
+
|
132
|
+
def duration(time_in_numeric)
|
133
|
+
secs = time_in_numeric.to_i
|
134
|
+
mins = secs / 60
|
135
|
+
hours = mins / 60
|
136
|
+
if hours > 0
|
137
|
+
"[#{hours}h #{mins % 60}m #{secs % 60}s]"
|
138
|
+
elsif mins > 0
|
139
|
+
"[#{mins}m #{secs % 60}s]"
|
140
|
+
elsif secs >= 0
|
141
|
+
"[0m #{secs}s]"
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
##
|
146
|
+
#
|
147
|
+
# Evaluates given value
|
148
|
+
#
|
149
|
+
# *Parameters:*
|
150
|
+
# * +value+ - Value to be evaluated
|
151
|
+
#
|
152
|
+
|
153
|
+
def ri(value)
|
154
|
+
raise value.inspect
|
155
|
+
end
|
156
|
+
|
157
|
+
private
|
158
|
+
|
159
|
+
def browser?(*browser_aliases)
|
160
|
+
if sauce_driver?
|
161
|
+
log.error Howitzer::SlBrowserNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.sl_browser_name.nil?
|
162
|
+
browser_aliases.include?(settings.sl_browser_name.to_s.to_sym)
|
163
|
+
elsif testingbot_driver?
|
164
|
+
log.error Howitzer::TbBrowserNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.tb_browser_name.nil?
|
165
|
+
browser_aliases.include?(settings.tb_browser_name.to_s.to_sym)
|
166
|
+
elsif selenium_driver? || selenium_grid_driver?
|
167
|
+
log.error Howitzer::SelBrowserNotSpecifiedError, CHECK_YOUR_SETTINGS_MSG if settings.sel_browser.nil?
|
168
|
+
browser_aliases.include?(settings.sel_browser.to_s.to_sym)
|
169
|
+
end
|
170
|
+
end
|
114
171
|
|
115
|
-
def ri(value)
|
116
|
-
raise value.inspect
|
117
172
|
end
|
118
173
|
|
174
|
+
include Helpers
|
175
|
+
|
119
176
|
class String
|
120
177
|
|
121
178
|
##
|
@@ -139,6 +196,11 @@ class String
|
|
139
196
|
as_page_class.given
|
140
197
|
end
|
141
198
|
|
199
|
+
##
|
200
|
+
#
|
201
|
+
# Waits until page is opened or raise error
|
202
|
+
#
|
203
|
+
|
142
204
|
def wait_for_opened
|
143
205
|
as_page_class.wait_for_opened
|
144
206
|
end
|
@@ -152,6 +214,11 @@ class String
|
|
152
214
|
as_class('Page')
|
153
215
|
end
|
154
216
|
|
217
|
+
##
|
218
|
+
#
|
219
|
+
# Returns email class
|
220
|
+
#
|
221
|
+
|
155
222
|
def as_email_class
|
156
223
|
as_class('Email')
|
157
224
|
end
|
@@ -23,7 +23,7 @@ module Mailgun
|
|
23
23
|
#
|
24
24
|
# @param [String] resource_path This is the API resource you wish to interact
|
25
25
|
# with. Be sure to include your domain, where necessary.
|
26
|
-
# @param [Hash]
|
26
|
+
# @param [Hash] params This should be a standard Hash for query
|
27
27
|
# containing required parameters for the requested resource.
|
28
28
|
# @return [Mailgun::Response] A Mailgun::Response object.
|
29
29
|
|
data/lib/howitzer/utils.rb
CHANGED
@@ -198,7 +198,7 @@ module LocatorStore
|
|
198
198
|
log.error Howitzer::BadLocatorParamsError, args.inspect if params.nil? || (!params.is_a?(Proc) && params.empty?)
|
199
199
|
case params.class.name
|
200
200
|
when 'Hash'
|
201
|
-
@locators[self.name][type][name] = [params.keys.first.to_sym, params.values.first.to_s]
|
201
|
+
@locators[self.name][type][name] = [params.keys.first.to_s.to_sym, params.values.first.to_s]
|
202
202
|
when 'Proc'
|
203
203
|
@locators[self.name][type][name] = params
|
204
204
|
else
|
data/lib/howitzer/utils/log.rb
CHANGED
@@ -93,7 +93,7 @@ module Howitzer
|
|
93
93
|
@logger = Logger.new("ruby_log")
|
94
94
|
@logger.add(console_log)
|
95
95
|
@logger.add(error_log)
|
96
|
-
@logger.add(txt_log)
|
96
|
+
@logger.add(txt_log) if !settings.log_dir.to_s.empty? && !settings.txt_log.to_s.empty?
|
97
97
|
self.base_formatter = default_formatter
|
98
98
|
Logger["ruby_log"].level = settings.debug_mode ? ALL : INFO
|
99
99
|
Logger["ruby_log"].trace = true
|
@@ -70,7 +70,7 @@ module Howitzer
|
|
70
70
|
def validates(name, options)
|
71
71
|
log.error TypeError, "Expected options to be Hash, actual is '#{options.class}'" unless options.class == Hash
|
72
72
|
PageValidator.validations[self.name] ||= {}
|
73
|
-
case name.to_sym
|
73
|
+
case name.to_s.to_sym
|
74
74
|
when :url
|
75
75
|
validate_by_pattern(:url, options)
|
76
76
|
when :element_presence
|
data/lib/howitzer/version.rb
CHANGED
data/lib/howitzer/web_page.rb
CHANGED
@@ -1,8 +1,8 @@
|
|
1
|
-
require
|
2
|
-
require
|
3
|
-
require
|
4
|
-
require
|
5
|
-
require
|
1
|
+
require 'singleton'
|
2
|
+
require 'rspec/expectations'
|
3
|
+
require 'howitzer/utils/locator_store'
|
4
|
+
require 'howitzer/utils/page_validator'
|
5
|
+
require 'howitzer/capybara/dsl_ex'
|
6
6
|
require 'howitzer/exceptions'
|
7
7
|
|
8
8
|
class WebPage
|
@@ -36,7 +36,7 @@ class WebPage
|
|
36
36
|
def self.open(url = "#{app_url unless self == BlankPage}#{self::URL}")
|
37
37
|
log.info "Open #{self.name} page by '#{url}' url"
|
38
38
|
retryable(tries: 2, logger: log, trace: true, on: Exception) do |retries|
|
39
|
-
log.info
|
39
|
+
log.info 'Retry...' unless retries.zero?
|
40
40
|
visit url
|
41
41
|
end
|
42
42
|
given
|
@@ -117,6 +117,7 @@ class WebPage
|
|
117
117
|
|
118
118
|
def initialize
|
119
119
|
check_validations_are_defined!
|
120
|
+
page.driver.browser.manage.window.maximize if settings.maximized_window
|
120
121
|
end
|
121
122
|
|
122
123
|
##
|
@@ -184,7 +185,7 @@ class WebPage
|
|
184
185
|
return true if page.evaluate_script('$.active') == 0
|
185
186
|
sleep 0.25
|
186
187
|
end
|
187
|
-
log.error message ||
|
188
|
+
log.error message || 'Timed out waiting for ajax requests to complete'
|
188
189
|
end
|
189
190
|
#:nocov:
|
190
191
|
|
@@ -199,7 +200,7 @@ class WebPage
|
|
199
200
|
#
|
200
201
|
|
201
202
|
def wait_for_url(expected_url, timeout=settings.timeout_small)
|
202
|
-
warn
|
203
|
+
warn '[Deprecated] This method is deprecated, and will be removed in next version of Howitzer'
|
203
204
|
end_time = ::Time.now + timeout
|
204
205
|
until ::Time.now > end_time
|
205
206
|
operator = expected_url.is_a?(Regexp) ? :=~ : :==
|
@@ -219,7 +220,7 @@ class WebPage
|
|
219
220
|
#
|
220
221
|
|
221
222
|
def wait_for_title(expected_title, timeout=settings.timeout_small)
|
222
|
-
warn
|
223
|
+
warn '[Deprecated] This method is deprecated, and will be removed in next version of Howitzer'
|
223
224
|
end_time = ::Time.now + timeout
|
224
225
|
until ::Time.now > end_time
|
225
226
|
operator = expected_title.is_a?(Regexp) ? :=~ : :==
|
@@ -249,5 +250,4 @@ class WebPage
|
|
249
250
|
def title
|
250
251
|
page.title
|
251
252
|
end
|
252
|
-
|
253
|
-
end
|
253
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -1,44 +1,33 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'bundler/setup'
|
3
3
|
require 'simplecov'
|
4
|
+
require 'coveralls'
|
4
5
|
require 'tmpdir'
|
5
6
|
require 'ffaker'
|
6
7
|
require 'capybara'
|
7
8
|
require 'json'
|
8
9
|
require 'capybara/dsl'
|
9
10
|
require 'active_support'
|
11
|
+
require 'active_support/deprecation'
|
12
|
+
require 'active_support/deprecation/method_wrappers'
|
10
13
|
require 'active_support/core_ext'
|
11
14
|
require 'repeater'
|
12
15
|
require 'howitzer/exceptions'
|
13
16
|
require 'howitzer/utils/log'
|
14
17
|
|
18
|
+
SimpleCov.formatter = SimpleCov::Formatter::MultiFormatter[
|
19
|
+
SimpleCov::Formatter::HTMLFormatter,
|
20
|
+
Coveralls::SimpleCov::Formatter
|
21
|
+
]
|
22
|
+
|
15
23
|
SimpleCov.start do
|
16
|
-
add_filter
|
24
|
+
add_filter '/spec/'
|
17
25
|
add_filter '/config/'
|
18
26
|
add_filter do |source_file|
|
19
27
|
source_file.lines.count < 5
|
20
28
|
end
|
21
|
-
add_group
|
22
|
-
add_group
|
23
|
-
end
|
24
|
-
|
25
|
-
Dir[File.join(File.dirname(__FILE__), 'support', '**', '*.rb')].each{ |f| require f }
|
26
|
-
|
27
|
-
RSpec.configure do |config|
|
28
|
-
config.include GeneratorHelper
|
29
|
-
config.expect_with :rspec do |c|
|
30
|
-
c.syntax = :expect
|
31
|
-
end
|
32
|
-
config.mock_with :rspec do |c|
|
33
|
-
c.syntax = :expect
|
34
|
-
end
|
35
|
-
config.around(:each) do |ex|
|
36
|
-
$stdout = StringIO.new
|
37
|
-
$stderr = StringIO.new
|
38
|
-
ex.run
|
39
|
-
$stdout = STDOUT
|
40
|
-
$stderr = STDERR
|
41
|
-
end
|
29
|
+
add_group 'generators', '/generators'
|
30
|
+
add_group 'lib', '/lib'
|
42
31
|
end
|
43
32
|
|
44
33
|
def project_path
|
@@ -56,3 +45,17 @@ end
|
|
56
45
|
def log_path
|
57
46
|
File.join(project_path, 'spec/log')
|
58
47
|
end
|
48
|
+
|
49
|
+
Dir[File.join(File.dirname(__FILE__), 'support', '**', '*.rb')].each{ |f| require f }
|
50
|
+
|
51
|
+
RSpec.configure do |config|
|
52
|
+
config.include GeneratorHelper
|
53
|
+
config.disable_monkey_patching = true
|
54
|
+
config.around(:each) do |ex|
|
55
|
+
$stdout = StringIO.new
|
56
|
+
$stderr = StringIO.new
|
57
|
+
ex.run
|
58
|
+
$stdout = STDOUT
|
59
|
+
$stderr = STDERR
|
60
|
+
end
|
61
|
+
end
|