report_builder 0.1.6 → 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 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