rspec_html_formatter2 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 13b1438fa46dd3c7163b1d795a6898385199b3ba
4
+ data.tar.gz: d11829308cb7083429f0cf93a85ddda7165f7ec1
5
+ SHA512:
6
+ metadata.gz: 2f19743c993bf015d0fca55e1f65a7d1ab95d1a5a85eb5c02322efb3cb858db286b2edb1c96992d0f20b8a8b06ee2442127b8cba0d1aed2e7db04c1af8576553
7
+ data.tar.gz: 636188e07d5ba5318582c4ed0a75fb55b43eece98d35093d4a20cf445343ba1d7776fcc32cbb9faadaa179eca7cc9a15ac2ce5e766310f4efce8d897ad79d3c0
data/LICENSE.txt ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2014 Kingsley Hendrickse
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,37 @@
1
+ # RSpec HTML Formatter2
2
+
3
+ Publish pretty [rspec](http://rspec.info/) reports
4
+
5
+ This is a ruby RSpec custom formatter which generates pretty html reports showing the results of rspec tests. It has features to embed images and videos into report providing better debugging information in case test is failed. Check this [Sample Report](https://vbanthia.github.io/rspec_html_formatter2/index.html).
6
+
7
+ ## Setup
8
+
9
+ Add this in your Gemfile:
10
+
11
+ ```rb
12
+ gem 'rspec_html_formatter2', git: 'https://github.com/vbanthia/rspec_html_formatter2'
13
+ ```
14
+ **Note:** Will publish it soon.
15
+
16
+ ## Running
17
+
18
+ Either add below in your `.rspec` file
19
+
20
+ ```rb
21
+ --format RspecHtmlFormatter
22
+ ```
23
+
24
+ or run RSpec with `--format RspecHtmlFormatter` like below:
25
+
26
+ ```bash
27
+ REPORT_PATH=reports/$(date +%s) bundle exec rspec --format RspecHtmlFormatter spec
28
+ ```
29
+
30
+ Above will create reports in `reports` directory.
31
+
32
+ ## Usage
33
+ Images and videos can be embed by adding their path into example's metadata. Check this [Sample Test](./spec/embed_graphics_spec.rb).
34
+
35
+
36
+ ## Credits
37
+ This library is forked from [kingsleyh/rspec_reports_formatter](https://github.com/kingsleyh/rspec_reports_formatter). Original Credits goes to *[kingsleyh](https://github.com/kingsleyh)*
@@ -0,0 +1,300 @@
1
+ require 'rspec/core/formatters/base_formatter'
2
+ require 'active_support'
3
+ require 'active_support/core_ext/numeric'
4
+ require 'active_support/inflector'
5
+ require 'fileutils'
6
+ require 'rouge'
7
+ require 'erb'
8
+ require 'rbconfig'
9
+
10
+ I18n.enforce_available_locales = false
11
+
12
+ class Oopsy
13
+ attr_reader :klass, :message, :backtrace, :highlighted_source, :explanation, :backtrace_message
14
+
15
+ def initialize(exception, file_path)
16
+ @exception = exception
17
+ @file_path = file_path
18
+ unless @exception.nil?
19
+ @klass = @exception.class
20
+ @message = @exception.message.encode('utf-8')
21
+ @backtrace = @exception.backtrace
22
+ @backtrace_message = @backtrace.nil? ? '' : @backtrace.select { |r| r.match(@file_path) }.join('').encode('utf-8')
23
+ @highlighted_source = process_source
24
+ @explanation = process_message
25
+ end
26
+ end
27
+
28
+ private
29
+
30
+ def os
31
+ @os ||= (
32
+ host_os = RbConfig::CONFIG['host_os']
33
+ case host_os
34
+ when /mswin|msys|mingw|cygwin|bccwin|wince|emc/
35
+ :windows
36
+ when /darwin|mac os/
37
+ :macosx
38
+ when /linux/
39
+ :linux
40
+ when /solaris|bsd/
41
+ :unix
42
+ else
43
+ raise Exception, "unknown os: #{host_os.inspect}"
44
+ end
45
+ )
46
+ end
47
+
48
+ def process_source
49
+ data = @backtrace_message.split(':')
50
+ unless data.empty?
51
+ if os == :windows
52
+ file_path = data[0] + ':' + data[1]
53
+ line_number = data[2].to_i
54
+ else
55
+ file_path = data.first
56
+ line_number = data[1].to_i
57
+ end
58
+ lines = File.readlines(file_path)
59
+ start_line = line_number-2
60
+ end_line = line_number+3
61
+ source = lines[start_line..end_line].join("").sub(lines[line_number-1].chomp, "--->#{lines[line_number-1].chomp}")
62
+
63
+ formatter = Rouge::Formatters::HTML.new(css_class: 'highlight', line_numbers: true, start_line: start_line+1)
64
+ lexer = Rouge::Lexers::Ruby.new
65
+ formatter.format(lexer.lex(source.encode('utf-8')))
66
+ end
67
+ end
68
+
69
+ def process_message
70
+ formatter = Rouge::Formatters::HTML.new(css_class: 'highlight')
71
+ lexer = Rouge::Lexers::Ruby.new
72
+ formatter.format(lexer.lex(@message))
73
+ end
74
+
75
+ end
76
+
77
+ class Example
78
+
79
+ attr_reader :example_group, :description, :full_description, :run_time, :duration, :status, :exception, :file_path, :metadata, :spec, :screenshots, :screenrecord, :failed_screenshot
80
+
81
+ def initialize(example)
82
+ @example_group = example.example_group.to_s
83
+ @description = example.description
84
+ @full_description = example.full_description
85
+ @execution_result = example.execution_result
86
+ @run_time = (@execution_result.run_time).round(5)
87
+ @duration = @execution_result.run_time.to_s(:rounded, precision: 5)
88
+ @status = @execution_result.status.to_s
89
+ @metadata = example.metadata
90
+ @file_path = @metadata[:file_path]
91
+ @exception = Oopsy.new(example.exception, @file_path)
92
+ @spec = nil
93
+ @screenshots = @metadata[:screenshots]
94
+ @screenrecord = @metadata[:screenrecord]
95
+ @failed_screenshot = @metadata[:failed_screenshot]
96
+ end
97
+
98
+ def example_title
99
+ title_arr = @example_group.to_s.split('::') - ['RSpec', 'ExampleGroups']
100
+ title_arr.push @description
101
+
102
+ title_arr.join(' → ')
103
+ end
104
+
105
+ def has_exception?
106
+ !@exception.klass.nil?
107
+ end
108
+
109
+ def has_spec?
110
+ !@spec.nil?
111
+ end
112
+
113
+ def has_screenshots?
114
+ !@screenshots.nil? && !@screenshots.empty?
115
+ end
116
+
117
+ def has_screenrecord?
118
+ !@screenrecord.nil?
119
+ end
120
+
121
+ def has_failed_screenshot?
122
+ !@failed_screenshot.nil?
123
+ end
124
+
125
+ def set_spec(spec)
126
+ @spec = spec
127
+ end
128
+
129
+ def klass(prefix='label-')
130
+ class_map = {passed: "#{prefix}success", failed: "#{prefix}danger", pending: "#{prefix}warning"}
131
+ class_map[@status.to_sym]
132
+ end
133
+
134
+ end
135
+
136
+ class Specify
137
+
138
+ def initialize(examples)
139
+ @examples = examples
140
+ end
141
+
142
+ def process
143
+ lines = File.readlines(@examples.first.file_path)
144
+ @examples.each_with_index do |e, i|
145
+ start_line = e.metadata[:line_number]
146
+ end_line = @examples[i+1].nil? ? lines.size : @examples[i+1].metadata[:line_number] - 1
147
+ code_block = lines[start_line..end_line]
148
+ spec = code_block.select { |l| l.match(/#->/) }.join('')
149
+ if !spec.split.empty?
150
+ formatter = Rouge::Formatters::HTML.new(css_class: 'highlight')
151
+ lexer = Rouge::Lexers::Gherkin.new
152
+ formatted_spec = formatter.format(lexer.lex(spec.gsub('#->', '')))
153
+ e.set_spec(formatted_spec)
154
+ end
155
+ end
156
+ @examples
157
+ end
158
+ end
159
+
160
+ class RspecHtmlFormatter < RSpec::Core::Formatters::BaseFormatter
161
+
162
+ DEFAULT_REPORT_PATH = File.join(Bundler.root, 'reports', Time.now.strftime('%Y%m%d-%H%M%S'))
163
+ REPORT_PATH = ENV['REPORT_PATH'] || DEFAULT_REPORT_PATH
164
+
165
+ SCREENRECORD_DIR = File.join(REPORT_PATH, 'screenrecords')
166
+ SCREENSHOT_DIR = File.join(REPORT_PATH, 'screenshots')
167
+ RESOURCE_DIR = File.join(REPORT_PATH, 'resources')
168
+
169
+ RSpec::Core::Formatters.register self, :example_started, :example_passed, :example_failed, :example_pending, :example_group_finished
170
+
171
+ def initialize(io_standard_out)
172
+ create_reports_dir
173
+ create_screenshots_dir
174
+ create_screenrecords_dir
175
+ copy_resources
176
+ @all_groups = {}
177
+
178
+ @group_level = 0
179
+ end
180
+
181
+ def example_group_started(notification)
182
+ if @group_level == 0
183
+ @example_group = {}
184
+ @group_examples = []
185
+ @group_example_count = 0
186
+ @group_example_success_count = 0
187
+ @group_example_failure_count = 0
188
+ @group_example_pending_count = 0
189
+ end
190
+
191
+ @group_level += 1
192
+ end
193
+
194
+ def example_started(notification)
195
+ @group_example_count += 1
196
+ end
197
+
198
+ def example_passed(notification)
199
+ @group_example_success_count += 1
200
+ @group_examples << Example.new(notification.example)
201
+ end
202
+
203
+ def example_failed(notification)
204
+ @group_example_failure_count += 1
205
+ @group_examples << Example.new(notification.example)
206
+ end
207
+
208
+ def example_pending(notification)
209
+ @group_example_pending_count += 1
210
+ @group_examples << Example.new(notification.example)
211
+ end
212
+
213
+ def example_group_finished(notification)
214
+ @group_level -= 1
215
+
216
+ if @group_level == 0
217
+ File.open("#{REPORT_PATH}/#{notification.group.description.parameterize}.html", 'w') do |f|
218
+
219
+ @passed = @group_example_success_count
220
+ @failed = @group_example_failure_count
221
+ @pending = @group_example_pending_count
222
+
223
+ duration_values = @group_examples.map { |e| e.run_time }
224
+
225
+ duration_keys = duration_values.size.times.to_a
226
+ if duration_values.size < 2 and duration_values.size > 0
227
+ duration_values.unshift(duration_values.first)
228
+ duration_keys = duration_keys << 1
229
+ end
230
+
231
+ @title = notification.group.description
232
+ @durations = duration_keys.zip(duration_values)
233
+
234
+ @summary_duration = duration_values.inject(0) { |sum, i| sum + i }.to_s(:rounded, precision: 5)
235
+ @examples = Specify.new(@group_examples).process
236
+
237
+ class_map = {passed: 'success', failed: 'danger', pending: 'warning'}
238
+ statuses = @examples.map { |e| e.status }
239
+ status = statuses.include?('failed') ? 'failed' : (statuses.include?('passed') ? 'passed' : 'pending')
240
+ @all_groups[notification.group.description.parameterize] = {
241
+ group: notification.group.description,
242
+ examples: @examples.size,
243
+ status: status,
244
+ klass: class_map[status.to_sym],
245
+ passed: statuses.select { |s| s == 'passed' },
246
+ failed: statuses.select { |s| s == 'failed' },
247
+ pending: statuses.select { |s| s == 'pending' },
248
+ duration: @summary_duration
249
+ }
250
+
251
+ template_file = File.read(File.dirname(__FILE__) + '/../templates/report.erb')
252
+
253
+ f.puts ERB.new(template_file).result(binding)
254
+ end
255
+ end
256
+ end
257
+
258
+ def close(notification)
259
+ File.open("#{REPORT_PATH}/overview.html", 'w') do |f|
260
+ @overview = @all_groups
261
+
262
+ @passed = @overview.values.map { |g| g[:passed].size }.inject(0) { |sum, i| sum + i }
263
+ @failed = @overview.values.map { |g| g[:failed].size }.inject(0) { |sum, i| sum + i }
264
+ @pending = @overview.values.map { |g| g[:pending].size }.inject(0) { |sum, i| sum + i }
265
+
266
+ duration_values = @overview.values.map { |e| e[:duration] }
267
+
268
+ duration_keys = duration_values.size.times.to_a
269
+ if duration_values.size < 2
270
+ duration_values.unshift(duration_values.first)
271
+ duration_keys = duration_keys << 1
272
+ end
273
+
274
+ @durations = duration_keys.zip(duration_values.map{|d| d.to_f.round(5)})
275
+ @summary_duration = duration_values.map{|d| d.to_f.round(5)}.inject(0) { |sum, i| sum + i }.to_s(:rounded, precision: 5)
276
+ @total_examples = @passed + @failed + @pending
277
+ template_file = File.read(File.dirname(__FILE__) + '/../templates/overview.erb')
278
+ f.puts ERB.new(template_file).result(binding)
279
+ end
280
+ end
281
+
282
+ private
283
+ def create_reports_dir
284
+ FileUtils.rm_rf(REPORT_PATH) if File.exists?(REPORT_PATH)
285
+ FileUtils.mkpath(REPORT_PATH)
286
+ end
287
+
288
+ def create_screenshots_dir
289
+ FileUtils.mkdir_p SCREENSHOT_DIR unless File.exists?(SCREENSHOT_DIR)
290
+ end
291
+
292
+ def create_screenrecords_dir
293
+ FileUtils.mkdir_p SCREENRECORD_DIR unless File.exists?(SCREENRECORD_DIR)
294
+ end
295
+
296
+ def copy_resources
297
+ FileUtils.cp_r(File.dirname(__FILE__) + '/../resources', REPORT_PATH)
298
+ end
299
+
300
+ end