report_builder 0.1.6 → 1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 4e3a824e02259c0bbfb4708f07ae0461ab094fbe
4
- data.tar.gz: 12cf431d094ecdea35d0e8f968dc99ae7d731965
3
+ metadata.gz: 330d350552316c577bff8a52a168924822c724e6
4
+ data.tar.gz: 5fb29cd31933f0233d164c468929da923a7abc98
5
5
  SHA512:
6
- metadata.gz: 86f54fb802bca18a855cd201b700876b7c58780713e8671398fcf660e963f36fc2353b67356c104aeefe5c4a63a6b7f4a3d5fb13017403ba0d9dda84c14268fb
7
- data.tar.gz: 65ad7213096af3316633aea421433e0da75ef49d81e5bbee6124724180e0112c711e47b3175a5601cd20d35b6d97e6ed489f892a315dfef1865210b4a1b31210
6
+ metadata.gz: ad9ca0719bc60179d1bd9368c3a9f27c57bcc53efdced8e5ed06b21ed8607f9b83d0782b651b207e716e43861a3a057211b55b0f313c5b81b77cfa7e2426b70b
7
+ data.tar.gz: 9e2aa291fa26ab62aacd0940ca006c66bcf0dcd48a891a958f7b001cafdf9c3e15e490bd0d82285aeb449c5f013ba7be90e4815e0d0ab3a76d5a216a90a07026
data/.gitignore CHANGED
@@ -1,4 +1,4 @@
1
- *.gem
1
+ pkg/
2
2
  .idea/
3
3
  .DS_Store
4
4
  temp/
data/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
- The MIT License (MIT)
1
+ MIT License
2
2
 
3
- Copyright (c) 2016 rajatthareja
3
+ Copyright (c) 2017 Rajat Thareja
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
data/README.md CHANGED
@@ -3,7 +3,9 @@
3
3
  [![Gem Version](https://badge.fury.io/rb/report_builder.svg)](https://badge.fury.io/rb/report_builder)
4
4
  [![Join the chat at https://gitter.im/rajatthareja/ReportBuilder](https://badges.gitter.im/rajatthareja/ReportBuilder.svg)](https://gitter.im/rajatthareja/ReportBuilder?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
5
5
 
6
- Ruby gem to merge Cucumber JSON reports and build single HTML Test Report
6
+ Ruby gem to merge Cucumber JSON reports and build mobile friendly HTML Test Report, JSON report and retry file.
7
+
8
+ [View sample report](http://reportbuilder.rajatthareja.com/)
7
9
 
8
10
  ## Installation
9
11
 
@@ -29,9 +31,8 @@ gem install report_builder
29
31
  | json_path | [String] / [Array] | (current directory) | json files path / array of json files or path |
30
32
  | report_path | [String] | 'test_report' | output file path with file name without extension |
31
33
  | report_types | [Array] | [:html] | :json, :html, :retry (output file types) |
32
- | report_tabs | [Array] | [:overview, :features] | :overview, :features, :scenarios, :errors (tabs to build) |
33
34
  | report_title | [String] | 'Test Results' | report and html title |
34
- | compress_images | [Boolean] | false | true / false (If true, the size of HTML report is reduced but takes more time to build report) |
35
+ | include_images | [Boolean] | true | true / false (If false, the size of HTML report is reduced by excluding embedded images) |
35
36
  | additional_info | [Hash] | {} | additional info for report summary |
36
37
 
37
38
  ### Code Examples:
@@ -45,9 +46,8 @@ gem install report_builder
45
46
  config.json_path = 'cucumber_sample/logs'
46
47
  config.report_path = 'my_test_report'
47
48
  config.report_types = [:json, :html]
48
- config.report_tabs = [:overview, :features, :scenarios, :errors]
49
49
  config.report_title = 'My Test Results'
50
- config.compress_images = false
50
+ config.include_images = false
51
51
  config.additional_info = {browser: 'Chrome', environment: 'Stage 5'}
52
52
  end
53
53
 
@@ -58,9 +58,8 @@ gem install report_builder
58
58
  json_path: 'cucumber_sample/logs',
59
59
  report_path: 'my_test_report',
60
60
  report_types: ['json', 'html'],
61
- report_tabs: [ 'overview', 'features', 'scenarios', 'errors'],
62
61
  report_title: 'My Test Results',
63
- compress_images: false,
62
+ include_images: false,
64
63
  additional_info: {'browser' => 'Chrome', 'environment' => 'Stage 5'}
65
64
  }
66
65
 
@@ -78,8 +77,7 @@ gem install report_builder
78
77
  | --html_out | [PATH]NAME | Same as the -o option but will only apply the html report format |
79
78
  | --retry_out | [PATH]NAME | Same as the -o option but will only apply the retry report format |
80
79
  | -f, --format | x,y,z | List of report format - html,json,retry |
81
- | -t, --tabs | x,y,z | List of report tabs - overview,features,scenarios,errors |
82
- | -c, --compress | | Reduce report size if embedding images |
80
+ | --[no-]images | | Reduce report size by excluding embedded images |
83
81
  | -T, --title | TITLE | Report title |
84
82
  | -I, --info | a:x,b:y,c:z | List of additional info about test - key:value |
85
83
  | -h, --help | | Show available command line switches |
@@ -92,7 +90,6 @@ gem install report_builder
92
90
  report_builder
93
91
  report_builder -s 'path/of/json/files/dir'
94
92
  report_builder -s 'path/of/json/files/dir' -o my_report_file
95
- report_builder -s 'path/of/json/files/dir' -o my_report_file -t overview,features,scenarios,errors
96
93
 
97
94
  ```
98
95
 
@@ -7,9 +7,8 @@ options = {
7
7
  :json_path => nil,
8
8
  :report_path => 'test_report',
9
9
  :report_types => [:html],
10
- :report_tabs => [:overview, :features],
11
10
  :report_title => 'Test Results',
12
- :compress_images => false,
11
+ :include_images => true,
13
12
  :additional_info => {}
14
13
  }
15
14
 
@@ -41,12 +40,8 @@ opt_parser = OptionParser.new do |opts|
41
40
  options[:report_types] = list
42
41
  end
43
42
 
44
- opts.on('-t','--tabs x,y,z', Array, 'List of report tabs - overview,features,scenarios,errors') do |list|
45
- options[:report_tabs] = list
46
- end
47
-
48
- opts.on('-c', '--compress', 'Reduce report size if embedding images') do |compress|
49
- options[:compress_images] = compress
43
+ opts.on('--[no-]images', 'Reduce report size by excluding embedded images') do |include_images|
44
+ options[:include_images] = include_images
50
45
  end
51
46
 
52
47
  opts.on('-T', '--title TITLE', String, 'Report title') do |report_title|
@@ -1,18 +1,21 @@
1
- require 'report_builder/builder'
1
+ require_relative 'report_builder/builder'
2
2
 
3
3
 
4
4
  module ReportBuilder
5
5
 
6
+ ##
7
+ # Configure ReportBuilder
6
8
  #
7
- # Ex: ReportBuilder.configure do |config|
9
+ # Example:
10
+ #
11
+ # ReportBuilder.configure do |config|
8
12
  # config.json_path = 'cucumber_sample/logs'
9
13
  # config.report_path = 'my_test_report'
10
14
  # config.report_types = [:JSON, :HTML]
11
- # config.report_tabs = [:Overview, :Features, :Scenarios, :Errors]
12
15
  # config.report_title = 'My Test Results'
13
- # config.compress_images = true
16
+ # config.include_images = true
14
17
  # config.additional_info = {Browser: 'Chrome', Environment: 'Stage 5'}
15
- # end
18
+ # end
16
19
  #
17
20
  def self.configure
18
21
  defaults = builder.default_options
@@ -20,16 +23,19 @@ module ReportBuilder
20
23
  builder.options = defaults.marshal_dump
21
24
  end
22
25
 
26
+ ##
27
+ # Build Report
23
28
  #
24
29
  # @param [Hash] options override the default and configured options.
25
30
  #
26
- # Ex: options = {
31
+ # Example:
32
+ #
33
+ # options = {
27
34
  # json_path: 'cucumber_sample/logs',
28
35
  # report_path: 'my_test_report',
29
36
  # report_types: ['json', 'html'],
30
- # report_tabs: [ 'overview', 'features', 'scenarios', 'errors'],
31
37
  # report_title: 'My Test Results',
32
- # compress_images: false,
38
+ # include_images: true,
33
39
  # additional_info: {'browser' => 'Chrome', 'environment' => 'Stage 5'}
34
40
  # }
35
41
  #
@@ -1,6 +1,8 @@
1
1
  require 'json'
2
- require 'builder'
2
+ require 'erb'
3
+ require 'pathname'
3
4
  require 'base64'
5
+
4
6
  require 'report_builder/core-ext/hash'
5
7
 
6
8
  module ReportBuilder
@@ -8,487 +10,86 @@ module ReportBuilder
8
10
 
9
11
  attr_accessor :options
10
12
 
11
-
12
- # colors corresponding to status
13
- COLOR = {
14
- passed: '#90ed7d',
15
- working: '#90ed7d',
16
- failed: '#f45b5b',
17
- broken: '#f45b5b',
18
- undefined: '#e4d354',
19
- incomplete: '#e7a35c',
20
- pending: '#f7a35c',
21
- skipped: '#7cb5ec',
22
- output: '#007fff'
23
- }
24
-
25
13
  def build_report(opts = nil)
26
14
  options = self.options || default_options.marshal_dump
27
15
  options.merge! opts if opts.is_a? Hash
28
16
 
29
- raise 'Error: Invalid report_types Use: [:json, :html]' unless options[:report_types].is_a? Array
30
- raise 'Error: Invalid report_tabs Use: [:overview, :features, :scenarios, :errors]' unless options[:report_tabs].is_a? Array
17
+ raise 'Error:: Invalid report_types. Use: [:json, :html]' unless options[:report_types].is_a? Array
31
18
 
32
19
  options[:report_types].map!(&:to_s).map!(&:upcase)
33
- options[:report_tabs].map!(&:to_s).map!(&:downcase)
34
-
35
- input = files options[:json_path]
36
- all_features = features input rescue (raise 'ReportBuilderParsingError')
37
-
38
- report_name = options[:json_report_path] || options[:report_path]
39
- File.open(report_name + '.json', 'w') do |file|
40
- file.write JSON.pretty_generate all_features
41
- end if options[:report_types].include? 'JSON'
42
-
43
- all_scenarios = scenarios all_features
44
- all_steps = steps all_scenarios
45
- all_tags = tags all_scenarios
46
- total_time = total_time all_features
47
- feature_data = data all_features
48
- scenario_data = data all_scenarios
49
- step_data = data all_steps
50
-
51
- report_name = options[:html_report_path] || options[:report_path]
52
- File.open(report_name + '.html', 'w:UTF-8') do |file|
53
- @builder = ::Builder::XmlMarkup.new(target: file, indent: 0)
54
- @builder.declare!(:DOCTYPE, :html)
55
- @builder << '<html>'
56
-
57
- @builder.head do
58
- @builder.meta(charset: 'UTF-8')
59
- @builder.title options[:report_title]
60
20
 
61
- @builder.style(type: 'text/css') do
62
- @builder << File.read(File.dirname(__FILE__) + '/../../vendor/assets/stylesheets/jquery-ui.min.css')
63
- COLOR.each do |color|
64
- @builder << ".#{color[0].to_s}{background:#{color[1]};color:#434348;padding:2px}"
65
- end
66
- @builder << '.summary{margin-bottom:4px;border: 1px solid #c5c5c5;border-radius:4px;background:#f1f1f1;color:#434348;padding:4px;overflow:hidden;vertical-align:bottom;}'
67
- @builder << '.summary .results{text-align:right;float:right;}'
68
- @builder << '.summary .info{text-align:left;float:left;}'
69
- @builder << '.data_table{border-collapse: collapse;} .data_table td{padding: 5px; border: 1px solid #ddd;}'
70
- @builder << '.ui-tooltip{background: black; color: white; font-size: 12px; padding: 2px 4px; border-radius: 20px; box-shadow: 0 0 7px black;}'
71
- end
72
-
73
- @builder.script(type: 'text/javascript') do
74
- %w(jquery-min jquery-ui.min highcharts highcharts-3d).each do |js|
75
- @builder << File.read(File.dirname(__FILE__) + '/../../vendor/assets/javascripts/' + js + '.js')
76
- end
77
- @builder << '$(function(){$("#results").tabs();});'
78
- @builder << "$(function(){$('#features').accordion({collapsible: true, heightStyle: 'content', active: false, icons: false});});"
79
- (0..all_features.size).each do |n|
80
- @builder << "$(function(){$('#feature#{n}').accordion({collapsible: true, heightStyle: 'content', active: false, icons: false});});"
81
- end
82
- @builder << "$(function(){$('#status').accordion({collapsible: true, heightStyle: 'content', active: false, icons: false});});"
83
- scenario_data.each do |data|
84
- @builder << "$(function(){$('##{data[:name]}').accordion({collapsible: true, heightStyle: 'content', active: false, icons: false});});"
85
- end
86
- @builder << '$(function() {$(document).tooltip({track: true});});'
87
- end
88
- end
21
+ files = get_files options[:json_path]
22
+ raise "Error:: No file(s) found at #{options[:json_path]}" if files.empty?
89
23
 
90
- @builder << '<body>'
24
+ features = get_features(files) rescue raise('Error:: Invalid Input File(s). Please provide valid cucumber JSON output file(s)')
91
25
 
92
- @builder.div(class: 'summary') do
93
- @builder.span(class: 'info') do
94
- info = options[:additional_info].empty?
95
- @builder << '<br/>&nbsp;&nbsp;&nbsp;' if info
96
- @builder.span(style: "font-size:#{info ? 36 : 18 }px;font-weight: bold;") do
97
- @builder << options[:report_title]
98
- end
99
- options[:additional_info].each do |l|
100
- @builder << '<br/>' + l[0].to_s.capitalize + ' : ' + l[1].to_s
101
- end
102
- end if options[:additional_info].is_a? Hash
103
- @builder.span(class: 'results') do
104
- s = all_features.size
105
- @builder << s.to_s + " feature#{'s' if s > 1} ("
106
- feature_data.each do |data|
107
- @builder << ' ' + data[:count].to_s + ' ' + data[:name]
108
- end
109
- s = all_scenarios.size
110
- @builder << ')<br/>' + s.to_s + " scenario#{'s' if s > 1} ("
111
- scenario_data.each do |data|
112
- @builder << ' ' + data[:count].to_s + ' ' + data[:name]
113
- end
114
- s = all_steps.size
115
- @builder << ')<br/>' + s.to_s + " step#{'s' if s > 1} ("
116
- step_data.each do |data|
117
- @builder << ' ' + data[:count].to_s + ' ' + data[:name]
118
- end
119
- @builder << ')<br/>&#128336; ' + duration(total_time).to_s
120
- end
121
- end
122
-
123
- @builder.div(id: 'results') do
124
- build_menu options[:report_tabs]
125
-
126
- @builder.div(id: 'overviewTab') do
127
- @builder << "<div id='featurePieChart' style=\"float:left;width:33%\"></div>"
128
- @builder << "<div id='scenarioPieChart' style=\"display:inline-block;width:33%\"></div>"
129
- @builder << "<div id='stepPieChart' style=\"float:right;width:33%\"></div>"
130
- end if options[:report_tabs].include? 'overview'
131
-
132
- @builder.div(id: 'featuresTab') do
133
- build_tags_drop_down(all_tags)
134
- @builder.div(id: 'features') do
135
- all_features.each_with_index do |feature, n|
136
- @builder.h3(style: "background:#{COLOR[feature['status'].to_sym]}") do
137
- @builder.span(class: feature['status']) do
138
- @builder << "<strong>#{feature['keyword']}</strong> #{feature['name']} (#{duration(feature['duration'])})"
139
- end
140
- end
141
- @builder.div do
142
- @builder.div(id: "feature#{n}") do
143
- feature['elements'].each {|scenario| build_scenario scenario}
144
- end
145
- end
146
- end
147
- end
148
- @builder << "<div id='featureTabPieChart'></div>"
149
- end if options[:report_tabs].include? 'features'
150
-
151
- @builder.div(id: 'scenariosTab') do
152
- build_tags_drop_down(all_tags)
153
- @builder.div(id: 'status') do
154
- all_scenarios.group_by {|scenario| scenario['status']}.each do |data|
155
- @builder.h3(style: "background:#{COLOR[data[0].to_sym]}") do
156
- @builder.span(class: data[0]) do
157
- @builder << "<strong>#{data[0].capitalize} scenarios (Count: <span id='count'>#{data[1].size}</span>)</strong>"
158
- end
159
- end
160
- @builder.div do
161
- @builder.div(id: data[0]) do
162
- data[1].sort_by {|scenario| scenario['name']}.each {|scenario| build_scenario scenario}
163
- end
164
- end
165
- end
166
- end
167
- @builder << "<div id='scenarioTabPieChart'></div>"
168
- end if options[:report_tabs].include? 'scenarios'
169
-
170
- @builder.div(id: 'errorsTab') do
171
- @builder.ol do
172
- all_scenarios.each {|scenario| build_error_list scenario}
173
- end
174
- end if options[:report_tabs].include? 'errors'
175
- end
176
-
177
- @builder.script(type: 'text/javascript') do
178
- @builder << pie_chart_js('featurePieChart', 'Features', feature_data) if options[:report_tabs].include? 'overview'
179
- @builder << donut_js('featureTabPieChart', 'Features', feature_data) if options[:report_tabs].include? 'features'
180
- @builder << pie_chart_js('scenarioPieChart', 'Scenarios', scenario_data) if options[:report_tabs].include? 'overview'
181
- @builder << donut_js('scenarioTabPieChart', 'Scenarios', scenario_data) if options[:report_tabs].include? 'scenarios'
182
- @builder << pie_chart_js('stepPieChart', 'Steps', step_data) if options[:report_tabs].include? 'overview'
183
- unless all_tags.empty?
184
- @builder << '$("#featuresTab .select-tags").change(function(){
185
- $("#featuresTab .scenario-all").hide().next().hide().parent().hide().parent().hide().prev().hide();
186
- $("#featuresTab ." + $(this).val()).show().parent().show().parent().prev().show();});' if options[:report_tabs].include? 'features'
187
- @builder << '$("#scenariosTab .select-tags").change(function(){var val = $(this).val();$("#scenariosTab .scenario-all").hide().next().hide();
188
- $("#scenariosTab ." + val).show();$("#scenariosTab #count").each(function(){status = $(this).parent().parent().prop("className");
189
- count = $("#scenariosTab #" + status + " ." + val).length;countElement = $("#scenariosTab ." + status + " #count");
190
- countElement.parent().parent().parent().show();if(count==0){countElement.parent().parent().parent().hide().next().hide();}
191
- countElement.html(count);});});' if options[:report_tabs].include? 'scenarios'
192
- end
193
- end
194
-
195
- @builder << '</body>'
196
- @builder << '</html>'
26
+ json_report_path = options[:json_report_path] || options[:report_path]
27
+ File.open(json_report_path + '.json', 'w') do |file|
28
+ file.write JSON.pretty_generate features
29
+ end if options[:report_types].include? 'JSON'
197
30
 
31
+ html_report_path = options[:html_report_path] || options[:report_path]
32
+ File.open(html_report_path + '.html', 'w') do |file|
33
+ file.write ERB.new(File.read(File.dirname(__FILE__) + '/../../template/html_report.erb')).result(binding).gsub(' ', '').gsub("\n\n", '')
198
34
  end if options[:report_types].include? 'HTML'
199
35
 
200
- report_name = options[:retry_report_path] || options[:report_path]
201
- File.open(report_name + '.retry', 'w:UTF-8') do |file|
202
- all_features.each do |feature|
36
+ retry_report_path = options[:retry_report_path] || options[:report_path]
37
+ File.open(retry_report_path + '.retry', 'w') do |file|
38
+ features.each do |feature|
203
39
  if feature['status'] == 'broken'
204
40
  feature['elements'].each {|scenario| file.puts "#{feature['uri']}:#{scenario['line']}" if scenario['status'] == 'failed'}
205
41
  end
206
42
  end
207
43
  end if options[:report_types].include? 'RETRY'
208
-
209
- [total_time, feature_data, scenario_data, step_data]
44
+ [json_report_path, html_report_path, retry_report_path]
210
45
  end
211
46
 
212
47
  def default_options
213
48
  OpenStruct.new(
214
- json_path: nil, # [String] / [Array] Input json file, array of json files/path or json files path, (Default current directory)
49
+ json_path: Dir.pwd, # [String] / [Array] Input json file, array of json files/path or json files path, (Default current directory)
215
50
  report_path: 'test_report', # [String] Output file path with name
216
51
  report_types: [:html], # [Array] Output file types to build, [:json, :html] or ['html', 'json']
217
- report_tabs: [:overview, :features], # [Array] Tabs to build, [:overview, :features, :scenarios, :errors] or ['overview', 'features', 'scenarios', 'errors']
218
52
  report_title: 'Test Results', # [String] Report and html title
219
- compress_images: false, # [Boolean] Set true to reducing the size of HTML report, Note: If true, takes more time to build report
220
- additional_info: {} # [Hash] Additional info for report summary
53
+ include_images: true, # [Boolean] Set false to reducing the size of HTML report, by excluding embedded images
54
+ additional_info: {} # [Hash] Additional info for report
221
55
  )
222
56
  end
223
57
 
224
- private
225
-
226
- def build_menu(tabs)
227
- @builder.ul do
228
- tabs.each do |tab|
229
- @builder.li do
230
- @builder.a(href: "##{tab}Tab") do
231
- @builder << tab.capitalize
232
- end
233
- end
234
- end
235
- end
236
- end
237
-
238
- def build_scenario(scenario)
239
- tags = (scenario['tags'] ? scenario['tags'].map {|tag| tag['name']}.join(' ') : '')
240
- @builder.h3(style: "background:#{COLOR[scenario['status'].to_sym]}", title: tags, class: 'scenario-all ' + tags.gsub('@', 'tag-')) do
241
- @builder.span(class: scenario['status']) do
242
- @builder << "<strong>#{scenario['keyword']}</strong> #{scenario['name']} (#{duration(scenario['duration'])})"
243
- end
244
- end
245
- @builder.div do
246
- scenario['before'].each do |before|
247
- build_hook_error before
248
- end
249
- scenario['steps'].each do |step|
250
- build_step step, scenario['keyword']
251
- end
252
- scenario['after'].each do |after|
253
- build_output after['output']
254
- build_hook_error after
255
- build_embedding after['embeddings']
256
- end
257
- end
258
- end
259
-
260
- def build_step(step, scenario_keyword)
261
- @builder.div(class: step['status']) do
262
- @builder << "<strong>#{step['keyword']}</strong> #{step['name']} (#{duration(step['duration'])})"
263
- end
264
- build_data_table step['rows']
265
- build_output step['output']
266
- build_step_error step
267
- build_embedding step['embeddings']
268
- step['after'].each do |after|
269
- build_output after['output']
270
- build_step_hook_error after, scenario_keyword
271
- build_embedding after['embeddings']
272
- end if step['after']
273
- end
274
-
275
- def build_data_table(rows)
276
- @builder.table(class: 'data_table', style: 'margin: 10px') do
277
- rows.each do |row|
278
- @builder.tr do
279
- row['cells'].each do |cell|
280
- @builder << "<td> #{cell} </td>"
281
- end
282
- end
283
- end
284
- end if rows.is_a? Array
285
- end
286
-
287
- def build_output(outputs)
288
- outputs.each do |output|
289
- @builder << "<span style='color:#{COLOR[:output]}'>#{output.to_s.gsub("\n", '</br>').gsub("\t", '&nbsp;&nbsp;').gsub(' ', '&nbsp;')}</span><br/>"
290
- end if outputs.is_a?(Array)
58
+ def decode(data)
59
+ Base64.decode64(data) rescue data
291
60
  end
292
61
 
293
- def build_tags_drop_down(tags)
294
- @builder.div(style: 'text-align:center;padding:5px;') do
295
- @builder << '<strong>Tag: </strong>'
296
- @builder.select(class: 'select-tags') do
297
- @builder.option(value: 'scenario-all') do
298
- @builder << 'All'
299
- end
300
- tags.sort.each do |tag|
301
- @builder.option(value: tag.gsub('@', 'tag-')) do
302
- @builder << tag
303
- end
304
- end
305
- end
306
- end if tags.is_a?(Array)
307
- end
308
-
309
- def build_step_error(step)
310
- if step['status'] == 'failed' && step['result']['error_message']
311
- @builder << "<strong style=color:#{COLOR[:failed]}>Error: </strong>"
312
- error = step['result']['error_message'].split("\n")
313
- @builder.span(style: "color:#{COLOR[:failed]}") do
314
- error[0..-3].each do |line|
315
- @builder << line + '<br/>'
316
- end
317
- end
318
- @builder << "<strong>SD: </strong>#{error[-2]} <br/>"
319
- @builder << "<strong>FF: </strong>#{error[-1]}<br/>"
320
- end
321
- end
322
-
323
- def build_hook_error(hook)
324
- if hook['status'] == 'failed'
325
- @builder << "<strong style=color:#{COLOR[:failed]}>Error: </strong>"
326
- error = hook['result']['error_message'].split("\n")
327
- @builder.span(style: "color:#{COLOR[:failed]}") do
328
- error[0..-2].each do |line|
329
- @builder << line + '<br/>'
330
- end
331
- end
332
- @builder << "<strong>Hook: </strong>#{error[-1]}<br/>"
333
- end
334
- end
335
-
336
- def build_step_hook_error(hook, scenario_keyword)
337
- if hook['result']['error_message']
338
- @builder << "<strong style=color:#{COLOR[:failed]}>Error: </strong>"
339
- error = hook['result']['error_message'].split("\n")
340
- @builder.span(style: "color:#{COLOR[:failed]}") do
341
- (scenario_keyword == 'Scenario Outline' ? error[0..-8] : error[0..-5]).each do |line|
342
- @builder << line + '<br/>'
343
- end
344
- end
345
- @builder << "<strong>Hook: </strong>#{scenario_keyword == 'Scenario Outline' ? error[-7] : error[-4]} <br/>"
346
- @builder << "<strong>FF: </strong>#{error[-2]}<br/>"
347
- end
348
- end
62
+ private
349
63
 
350
- def build_embedding(embeddings)
351
- @embedding_count ||= 0
352
- embeddings.each do |embedding|
353
- src = Base64.decode64(embedding['data'])
354
- id = "embedding_#{@embedding_count}"
355
- if embedding['mime_type'] =~ /^image\/(png|gif|jpg|jpeg)/
356
- begin
357
- @builder.span(class: 'image') do
358
- @builder.a(href: '', style: 'text-decoration: none;', onclick: "img=document.getElementById('#{id}');img.style.display = (img.style.display == 'none' ? 'block' : 'none');return false") do
359
- @builder.span(style: "color: #{COLOR[:output]}; font-weight: bold; border-bottom: 1px solid #{COLOR[:output]};") do
360
- @builder << "Screenshot ##{@embedding_count}"
361
- end
362
- end
363
- @builder << '<br/>'
364
- options[:compress_images] ? build_unique_image(embedding, id) : build_image(embedding, id)
365
- end
366
- rescue => e
367
- puts 'Image embedding failed!'
368
- puts [e.class, e.message, e.backtrace[0..10].join("\n")].join("\n")
369
- end
370
- elsif embedding['mime_type'] =~ /^text\/plain/
371
- begin
372
- if src.include?('|||')
373
- title, link = src.split('|||')
374
- @builder.span(class: 'link') do
375
- @builder.a(id: id, style: 'text-decoration: none;', href: link, title: title) do
376
- @builder.span(style: "color: #{COLOR[:output]}; font-weight: bold; border-bottom: 1px solid #{COLOR[:output]};") do
377
- @builder << title
378
- end
379
- end
380
- @builder << '<br/>'
381
- end
64
+ def get_files(path)
65
+ if path.is_a?(String) and Pathname.new(path).exist?
66
+ if Pathname.new(path).directory?
67
+ Dir.glob("#{path}/*.json")
68
+ else
69
+ [path]
70
+ end
71
+ elsif path.is_a? Array
72
+ path.map do |file|
73
+ if Pathname.new(file).exist?
74
+ if Pathname.new(file).directory?
75
+ Dir.glob("#{file}/*.json")
382
76
  else
383
- @builder.span(class: 'info') do
384
- @builder << src
385
- @builder << '<br/>'
386
- end
77
+ file
387
78
  end
388
- rescue => e
389
- puts('Link embedding skipped!')
390
- puts [e.class, e.message, e.backtrace[0..10].join("\n")].join("\n")
79
+ else
80
+ []
391
81
  end
392
- end
393
- @embedding_count += 1
394
- end if embeddings.is_a?(Array)
395
- end
396
-
397
- def build_unique_image(image, id)
398
- @images ||= []
399
- index = @images.find_index image
400
- if index
401
- klass = "image_#{index}"
82
+ end.flatten
402
83
  else
403
- @images << image
404
- klass = "image_#{@images.size - 1}"
405
- @builder.style(type: 'text/css') do
406
- begin
407
- src = Base64.decode64(image['data'])
408
- src = 'data:' + image['mime_type'] + ';base64,' + src unless src =~ /^data:image\/(png|gif|jpg|jpeg);base64,/
409
- @builder << "img.#{klass} {content: url(#{src});}"
410
- rescue
411
- src = image['data']
412
- src = 'data:' + image['mime_type'] + ';base64,' + src unless src =~ /^data:image\/(png|gif|jpg|jpeg);base64,/
413
- @builder << "img.#{klass} {content: url(#{src});}"
414
- end
415
- end
416
- end
417
- @builder << %{<img id='#{id}' class='#{klass}' style='display: none; border: 1px solid #{COLOR[:output]};' />}
418
- end
419
-
420
- def build_image(image, id)
421
- begin
422
- src = Base64.decode64(image['data'])
423
- src = 'data:' + image['mime_type'] + ';base64,' + src unless src =~ /^data:image\/(png|gif|jpg|jpeg);base64,/
424
- @builder << %{<img id='#{id}' style='display: none; border: 1px solid #{COLOR[:output]};' src='#{src}'/>}
425
- rescue
426
- src = image['data']
427
- src = 'data:' + image['mime_type'] + ';base64,' + src unless src =~ /^data:image\/(png|gif|jpg|jpeg);base64,/
428
- @builder << %{<img id='#{id}' style='display: none; border: 1px solid #{COLOR[:output]};' src='#{src}'/>}
429
- end
430
- end
431
-
432
- def build_error_list(scenario)
433
- scenario['before'].each do |before|
434
- next unless before['status'] == 'failed'
435
- @builder.li do
436
- error = before['result']['error_message'].split("\n")
437
- @builder.span(style: "color:#{COLOR[:failed]}") do
438
- error[0..-2].each do |line|
439
- @builder << line + '<br/>'
440
- end
441
- end
442
- @builder << "<strong>Hook: </strong>#{error[-1]} <br/>"
443
- @builder << "<strong>Scenario: </strong>#{scenario['name']} <br/><hr/>"
444
- end
445
- end
446
- scenario['steps'].each do |step|
447
- step['after'].each do |after|
448
- next unless after['status'] == 'failed'
449
- @builder.li do
450
- error = after['result']['error_message'].split("\n")
451
- @builder.span(style: "color:#{COLOR[:failed]}") do
452
- (scenario['keyword'] == 'Scenario Outline' ? error[0..-8] : error[0..-5]).each do |line|
453
- @builder << line + '<br/>'
454
- end
455
- end
456
- @builder << "<strong>Hook: </strong>#{scenario['keyword'] == 'Scenario Outline' ? error[-7] : error[-4]} <br/>"
457
- @builder << "<strong>FF: </strong>#{error[-2]} <br/><hr/>"
458
- end
459
- end if step['after']
460
- next unless step['status'] == 'failed' && step['result']['error_message']
461
- @builder.li do
462
- error = step['result']['error_message'].split("\n")
463
- @builder.span(style: "color:#{COLOR[:failed]}") do
464
- error[0..-3].each do |line|
465
- @builder << line + '<br/>'
466
- end
467
- end
468
- @builder << "<strong>SD: </strong>#{error[-2]} <br/>"
469
- @builder << "<strong>FF: </strong>#{error[-1]} <br/><hr/>"
470
- end
471
- end
472
- scenario['after'].each do |after|
473
- next unless after['status'] == 'failed'
474
- @builder.li do
475
- error = after['result']['error_message'].split("\n")
476
- @builder.span(style: "color:#{COLOR[:failed]}") do
477
- error[0..-2].each do |line|
478
- @builder << line + '<br/>'
479
- end
480
- end
481
- @builder << "<strong>Hook: </strong>#{error[-1]} <br/>"
482
- @builder << "<strong>Scenario: </strong>#{scenario['name']} <br/><hr/>"
483
- end
484
- end
84
+ []
85
+ end.uniq
485
86
  end
486
87
 
487
- def features(files)
88
+ def get_features(files)
488
89
  files.each_with_object([]) {|file, features|
489
90
  data = File.read(file)
490
91
  next if data.empty?
491
- features << JSON.parse(data)
92
+ features << JSON.parse(data) rescue next
492
93
  }.flatten.group_by {|feature|
493
94
  feature['uri']+feature['id']+feature['line'].to_s
494
95
  }.values.each_with_object([]) {|group, features|
@@ -498,8 +99,10 @@ module ReportBuilder
498
99
  (0..feature['elements'].size-1).step(2) do |i|
499
100
  feature['elements'][i]['steps'] ||= []
500
101
  feature['elements'][i]['steps'].each {|step| step['name']+=(' ('+feature['elements'][i]['keyword']+')')}
501
- feature['elements'][i+1]['steps'] = feature['elements'][i]['steps'] + feature['elements'][i+1]['steps']
502
- feature['elements'][i+1]['before'] = feature['elements'][i]['before'] if feature['elements'][i]['before']
102
+ if feature['elements'][i+1]
103
+ feature['elements'][i+1]['steps'] = feature['elements'][i]['steps'] + feature['elements'][i+1]['steps']
104
+ feature['elements'][i+1]['before'] = feature['elements'][i]['before'] if feature['elements'][i]['before']
105
+ end
503
106
  end
504
107
  feature['elements'].reject! {|element| element['type'] == 'background'}
505
108
  end
@@ -543,12 +146,6 @@ module ReportBuilder
543
146
  feature_status
544
147
  end
545
148
 
546
- def scenarios(features)
547
- features.map do |feature|
548
- feature['elements']
549
- end.flatten
550
- end
551
-
552
149
  def scenario_status(scenario)
553
150
  (scenario['before'] + scenario['steps'] + scenario['after']).each do |step|
554
151
  status = step['status']
@@ -557,42 +154,6 @@ module ReportBuilder
557
154
  'passed'
558
155
  end
559
156
 
560
- def steps(scenarios)
561
- scenarios.map do |scenario|
562
- scenario['steps']
563
- end.flatten
564
- end
565
-
566
- def tags(scenarios)
567
- scenarios.map do |scenario|
568
- scenario['tags'] ? scenario['tags'].map {|t| t['name']} : []
569
- end.flatten.uniq
570
- end
571
-
572
- def files(path)
573
- files = if path.is_a? String
574
- (path =~ /\.json$/) ? [path] : Dir.glob("#{path}/*.json")
575
- elsif path.nil?
576
- Dir.glob('*.json')
577
- elsif path.is_a? Array
578
- path.map do |file|
579
- (file =~ /\.json$/) ? file : Dir.glob("#{file}/*.json")
580
- end.flatten
581
- else
582
- raise 'InvalidInput'
583
- end
584
- raise 'InvalidOrNoInputFile' if files.empty?
585
- files.uniq
586
- end
587
-
588
- def data(all_data)
589
- all_data.group_by {|db| db['status']}.map do |data|
590
- {name: data[0],
591
- count: data[1].size,
592
- color: COLOR[data[0].to_sym]}
593
- end
594
- end
595
-
596
157
  def total_time(data)
597
158
  total_time = 0
598
159
  data.each {|item| total_time += item['duration']}
@@ -604,42 +165,5 @@ module ReportBuilder
604
165
  m, s = seconds.divmod(60)
605
166
  "#{m}m #{'%.3f' % s}s"
606
167
  end
607
-
608
- def pie_chart_js(id, title, data)
609
- data = data.each_with_object('') do |h, s|
610
- s << "{name: '#{h[:name].capitalize}'"
611
- s << ",y: #{h[:count]}"
612
- s << ',sliced: true' if h[:sliced]
613
- s << ',selected: true' if h[:selected]
614
- s << ",color: '#{h[:color]}'" if h[:color]
615
- s << '},'
616
- end.chop
617
- "$(function (){$('##{id}').highcharts({credits: {enabled: false}, chart: {type: 'pie',
618
- options3d: {enabled: true, alpha: 45, beta: 0}}, title: {text: '#{title}'},
619
- tooltip: {pointFormat: 'Count: <b>{point.y}</b><br/>Percentage: <b>{point.percentage:.1f}%</b>'},
620
- plotOptions: {pie: {allowPointSelect: true, cursor: 'pointer', depth: 35, dataLabels: {enabled: true,
621
- format: '{point.name}'}}}, series: [{type: 'pie', name: 'Results', data: [#{data}]}]});});"
622
- end
623
-
624
- def donut_js(id, title, data)
625
- data = data.each_with_object('') do |h, s|
626
- s << "{name: '#{h[:name].capitalize}'"
627
- s << ",y: #{h[:count]}"
628
- s << ',sliced: true' if h[:sliced]
629
- s << ',selected: true' if h[:selected]
630
- s << ",color: '#{h[:color]}'" if h[:color]
631
- s << '},'
632
- end.chop
633
- "$(function (){$('##{id}').highcharts({credits: {enabled: false},
634
- chart: {plotBackgroundColor: null, plotBorderWidth: 0, plotShadow: false, width: $(document).width()-80},
635
- title: {text: '#{title}', align: 'center', verticalAlign: 'middle', y: 40},
636
- tooltip: {pointFormat: 'Count: <b>{point.y}</b><br/>Percentage: <b>{point.percentage:.1f}%</b>'},
637
- plotOptions: {pie: {dataLabels: {enabled: true, distance: -50,
638
- style: {fontWeight: 'bold', color: 'white', textShadow: '0px 1px 2px black'}},
639
- startAngle: -90, endAngle: 90, center: ['50%', '75%']}},
640
- series: [{type: 'pie', innerSize: '50%', name: 'Results', data: [#{data}]}]});});"
641
- end
642
-
643
168
  end
644
-
645
169
  end