report_builder_fix 0.3 → 0.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/report_builder +1 -1
- data/bin/report_builder_fix +1 -1
- data/lib/.DS_Store +0 -0
- data/lib/report_builder/builder.rb +284 -0
- data/lib/report_builder/core-ext/hash.rb +10 -0
- data/lib/report_builder.rb +429 -0
- data/template/error.erb +21 -0
- data/template/errors.erb +5 -0
- data/template/feature.erb +16 -0
- data/template/features.erb +6 -0
- data/template/footer.erb +46 -0
- data/template/group_errors.erb +18 -0
- data/template/group_features.erb +18 -0
- data/template/group_overview.erb +33 -0
- data/template/group_report.erb +39 -0
- data/template/group_summary.erb +25 -0
- data/template/head.erb +22 -0
- data/template/header.erb +29 -0
- data/template/hook.erb +31 -0
- data/template/overview.erb +29 -0
- data/template/report.erb +37 -0
- data/template/scenario.erb +35 -0
- data/template/scenarios.erb +14 -0
- data/template/step.erb +32 -0
- data/template/summary.erb +27 -0
- data/template/summary_row.erb +35 -0
- metadata +29 -14
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c7e0aa0fce00a40e7f73b0caf3afab44b258df6a8b0ac85b4e03a341ed8cc89
|
4
|
+
data.tar.gz: c2a0627eb5ad90c8d80886b554be1e34d718dea3893060010b392112c8e19d80
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 97200d64868e061f0a59d8e4dd89d6552e521e4d5994fb6780ef7e7c5afeb7d745bd56c0ce6602dd24866f7871e4784e773e5208bd9c36daac12ea109ba3a573
|
7
|
+
data.tar.gz: dedaacfc0f2a12294a5f7f9a2c70bf726e505f8d2c609272e129af750c1d6baae057881da6813b81ce82d997b4d8e44cfa09db1705f7f766fe1c001bf3b16fea
|
data/bin/report_builder
CHANGED
@@ -74,7 +74,7 @@ opt_parser = OptionParser.new do |opts|
|
|
74
74
|
end
|
75
75
|
|
76
76
|
opts.on_tail('-v', '--version', 'Show version') do
|
77
|
-
puts '
|
77
|
+
puts 'ReportBuilderFix v' + Gem.loaded_specs['report_builder_fix'].version.to_s rescue puts "Something want wrong. \nUse 'gem list report_builder'"
|
78
78
|
exit
|
79
79
|
end
|
80
80
|
end
|
data/bin/report_builder_fix
CHANGED
@@ -74,7 +74,7 @@ opt_parser = OptionParser.new do |opts|
|
|
74
74
|
end
|
75
75
|
|
76
76
|
opts.on_tail('-v', '--version', 'Show version') do
|
77
|
-
puts '
|
77
|
+
puts 'ReportBuilderFix v' + Gem.loaded_specs['report_builder_fix'].version.to_s rescue puts "Something want wrong. \nUse 'gem list report_builder'"
|
78
78
|
exit
|
79
79
|
end
|
80
80
|
end
|
data/lib/.DS_Store
CHANGED
Binary file
|
@@ -0,0 +1,284 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'erb'
|
3
|
+
require 'pathname'
|
4
|
+
require 'base64'
|
5
|
+
require 'ostruct'
|
6
|
+
|
7
|
+
require 'report_builder/core-ext/hash'
|
8
|
+
|
9
|
+
module ReportBuilder
|
10
|
+
|
11
|
+
##
|
12
|
+
# ReportBuilder Main class
|
13
|
+
#
|
14
|
+
class Builder
|
15
|
+
|
16
|
+
##
|
17
|
+
# ReportBuilder Main method
|
18
|
+
#
|
19
|
+
def build_report
|
20
|
+
options = ReportBuilder.options
|
21
|
+
|
22
|
+
groups = get_groups options[:input_path]
|
23
|
+
|
24
|
+
json_report_path = options[:json_report_path] || options[:report_path]
|
25
|
+
if options[:report_types].include? 'JSON'
|
26
|
+
File.open(json_report_path + '.json', 'w') do |file|
|
27
|
+
file.write JSON.pretty_generate(groups.size > 1 ? groups : groups.first['features'])
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
if options[:additional_css] and Pathname.new(options[:additional_css]).file?
|
32
|
+
options[:additional_css] = File.read(options[:additional_css])
|
33
|
+
end
|
34
|
+
|
35
|
+
if options[:additional_js] and Pathname.new(options[:additional_js]).file?
|
36
|
+
options[:additional_js] = File.read(options[:additional_js])
|
37
|
+
end
|
38
|
+
|
39
|
+
html_report_path = options[:html_report_path] || options[:report_path]
|
40
|
+
if options[:report_types].include? 'HTML'
|
41
|
+
File.open(html_report_path + '.html', 'w') do |file|
|
42
|
+
file.write get(groups.size > 1 ? 'group_report' : 'report').result(binding)
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
retry_report_path = options[:retry_report_path] || options[:report_path]
|
47
|
+
if options[:report_types].include? 'RETRY'
|
48
|
+
File.open(retry_report_path + '.retry', 'w') do |file|
|
49
|
+
groups.each do |group|
|
50
|
+
group['features'].each do |feature|
|
51
|
+
if feature['status'] == 'broken'
|
52
|
+
feature['elements'].each do |scenario|
|
53
|
+
file.puts "#{feature['uri']}:#{scenario['line']}" if scenario['status'] == 'failed'
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
end
|
60
|
+
[json_report_path, html_report_path, retry_report_path]
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
|
65
|
+
def get(template)
|
66
|
+
@erb ||= {}
|
67
|
+
template_text = File.read(File.dirname(__FILE__) + '/../../template/' + template + '.erb')
|
68
|
+
|
69
|
+
# TODO: Remove once older Rubies are not longer supported
|
70
|
+
# Only use the deprecated method parameters for older versions of Ruby
|
71
|
+
if RUBY_VERSION =~ /^2\.[012345]/
|
72
|
+
@erb[template] ||= ERB.new(template_text, nil, nil, '_' + template)
|
73
|
+
else
|
74
|
+
@erb[template] ||= ERB.new(template_text, eoutvar: '_' + template)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def get_groups(input_path)
|
79
|
+
groups = []
|
80
|
+
if input_path.is_a? Hash
|
81
|
+
input_path.each do |group_name, group_path|
|
82
|
+
files = get_files group_path
|
83
|
+
puts "Error:: No file(s) found at #{group_path}" if files.empty?
|
84
|
+
groups << {'name' => group_name, 'features' => get_features(files)}
|
85
|
+
end
|
86
|
+
else
|
87
|
+
files = get_files input_path
|
88
|
+
raise "Error:: No file(s) found at #{input_path}" if files.empty?
|
89
|
+
groups << {'features' => get_features(files)}
|
90
|
+
end
|
91
|
+
groups.each do |this_group|
|
92
|
+
this_group['features'].each do |feature|
|
93
|
+
feature['elements'] = feature['elements'].sort_by { |v| v['line']}
|
94
|
+
end
|
95
|
+
end
|
96
|
+
groups
|
97
|
+
end
|
98
|
+
|
99
|
+
def get_files(path)
|
100
|
+
if path.is_a?(String) and Pathname.new(path).exist?
|
101
|
+
if Pathname.new(path).directory?
|
102
|
+
Dir.glob("#{path}/*.json")
|
103
|
+
else
|
104
|
+
[path]
|
105
|
+
end
|
106
|
+
elsif path.is_a? Array
|
107
|
+
path.map do |file|
|
108
|
+
if Pathname.new(file).exist?
|
109
|
+
if Pathname.new(file).directory?
|
110
|
+
Dir.glob("#{file}/*.json")
|
111
|
+
else
|
112
|
+
file
|
113
|
+
end
|
114
|
+
else
|
115
|
+
[]
|
116
|
+
end
|
117
|
+
end.flatten
|
118
|
+
else
|
119
|
+
[]
|
120
|
+
end.uniq
|
121
|
+
end
|
122
|
+
|
123
|
+
def get_features(files)
|
124
|
+
files.each_with_object([]) do |file, features|
|
125
|
+
data = File.read(file)
|
126
|
+
next if data.empty?
|
127
|
+
begin
|
128
|
+
features << JSON.parse(data)
|
129
|
+
rescue StandardError
|
130
|
+
puts 'Warning:: Invalid Input File ' + file
|
131
|
+
puts 'JSON Error:: ' + $!.to_s
|
132
|
+
next
|
133
|
+
end
|
134
|
+
end.flatten.group_by do |feature|
|
135
|
+
feature['uri'] + feature['id'] + feature['line'].to_s
|
136
|
+
end.values.each_with_object([]) do |group, features|
|
137
|
+
features << group.first.except('elements').merge('elements' => group.map {|feature| feature['elements']}.flatten)
|
138
|
+
end.sort_by! do |feature|
|
139
|
+
feature['name']
|
140
|
+
end.each do |feature|
|
141
|
+
feature['name'] = ERB::Util.html_escape feature['name']
|
142
|
+
if feature['elements'][0]['type'] == 'background'
|
143
|
+
(0..feature['elements'].size-1).step(2) do |i|
|
144
|
+
feature['elements'][i]['steps'] ||= []
|
145
|
+
feature['elements'][i]['steps'].each {|step| step['name'] += (' (' + feature['elements'][i]['keyword'] + ')')}
|
146
|
+
if feature['elements'][i+1]
|
147
|
+
feature['elements'][i+1]['steps'] = feature['elements'][i]['steps'] + feature['elements'][i+1]['steps']
|
148
|
+
feature['elements'][i+1]['before'] = feature['elements'][i]['before'] if feature['elements'][i]['before']
|
149
|
+
end
|
150
|
+
end
|
151
|
+
feature['elements'].reject! do |element|
|
152
|
+
element['type'] == 'background'
|
153
|
+
end
|
154
|
+
end
|
155
|
+
feature['elements'].each do |scenario|
|
156
|
+
scenario['name'] = ERB::Util.html_escape scenario['name']
|
157
|
+
scenario['before'] ||= []
|
158
|
+
scenario['before'].each do |before|
|
159
|
+
before['result']['duration'] ||= 0
|
160
|
+
if before['embeddings']
|
161
|
+
before['embeddings'].map! do |embedding|
|
162
|
+
decode_embedding(embedding)
|
163
|
+
end
|
164
|
+
end
|
165
|
+
before.merge! 'status' => before['result']['status'], 'duration' => before['result']['duration']
|
166
|
+
end
|
167
|
+
scenario['steps'] ||= []
|
168
|
+
scenario['steps'].each do |step|
|
169
|
+
step['name'] = ERB::Util.html_escape step['name']
|
170
|
+
step['result']['duration'] ||= 0
|
171
|
+
duration = step['result']['duration']
|
172
|
+
status = step['result']['status']
|
173
|
+
if step['after']
|
174
|
+
step['after'].each do |after|
|
175
|
+
after['result']['duration'] ||= 0
|
176
|
+
duration += after['result']['duration']
|
177
|
+
status = 'failed' if after['result']['status'] == 'failed'
|
178
|
+
if after['embeddings']
|
179
|
+
after['embeddings'].map! do |embedding|
|
180
|
+
decode_embedding(embedding)
|
181
|
+
end
|
182
|
+
end
|
183
|
+
after.merge! 'status' => after['result']['status'], 'duration' => after['result']['duration']
|
184
|
+
end
|
185
|
+
end
|
186
|
+
if step['embeddings']
|
187
|
+
step['embeddings'].map! do |embedding|
|
188
|
+
decode_embedding(embedding)
|
189
|
+
end
|
190
|
+
end
|
191
|
+
step.merge! 'status' => status, 'duration' => duration
|
192
|
+
end
|
193
|
+
scenario['after'] ||= []
|
194
|
+
scenario['after'].each do |after|
|
195
|
+
after['result']['duration'] ||= 0
|
196
|
+
if after['embeddings']
|
197
|
+
after['embeddings'].map! do |embedding|
|
198
|
+
decode_embedding(embedding)
|
199
|
+
end
|
200
|
+
end
|
201
|
+
after.merge! 'status' => after['result']['status'], 'duration' => after['result']['duration']
|
202
|
+
end
|
203
|
+
scenario.merge! 'status' => scenario_status(scenario), 'duration' => total_time(scenario['before']) + total_time(scenario['steps']) + total_time(scenario['after'])
|
204
|
+
end
|
205
|
+
feature['elements'] = feature['elements'].group_by do |scenario|
|
206
|
+
scenario['id'] + ':' + scenario['line'].to_s
|
207
|
+
end.values.map do |scenario_group|
|
208
|
+
the_scenario = scenario_group.find do |scenario|
|
209
|
+
scenario['status'] == 'passed'
|
210
|
+
end || scenario_group.last
|
211
|
+
if scenario_group.size > 1
|
212
|
+
the_scenario['name'] += " (x#{scenario_group.size})"
|
213
|
+
end
|
214
|
+
the_scenario
|
215
|
+
end
|
216
|
+
feature.merge! 'status' => feature_status(feature), 'duration' => total_time(feature['elements'])
|
217
|
+
end
|
218
|
+
end
|
219
|
+
|
220
|
+
def feature_status(feature)
|
221
|
+
feature_status = 'working'
|
222
|
+
feature['elements'].each do |scenario|
|
223
|
+
status = scenario['status']
|
224
|
+
return 'broken' if status == 'failed'
|
225
|
+
feature_status = 'incomplete' if %w(undefined pending).include?(status)
|
226
|
+
end
|
227
|
+
feature_status
|
228
|
+
end
|
229
|
+
|
230
|
+
def scenario_status(scenario)
|
231
|
+
(scenario['before'] + scenario['steps'] + scenario['after']).each do |step|
|
232
|
+
status = step['status']
|
233
|
+
return status unless status == 'passed'
|
234
|
+
end
|
235
|
+
'passed'
|
236
|
+
end
|
237
|
+
|
238
|
+
def decode_image(data)
|
239
|
+
base64 = %r{^([A-Za-z0-9+\/]{4})*([A-Za-z0-9+\/]{4}|[A-Za-z0-9+\/]{3}=|[A-Za-z0-9+\/]{2}==)$}
|
240
|
+
if data =~ base64
|
241
|
+
data_base64 = Base64.urlsafe_decode64(data).gsub(%r{^data:image\/(png|gif|jpg|jpeg)\;base64,}, '') rescue data
|
242
|
+
if data_base64 =~ base64
|
243
|
+
data_base64
|
244
|
+
else
|
245
|
+
data
|
246
|
+
end
|
247
|
+
else
|
248
|
+
''
|
249
|
+
end
|
250
|
+
end
|
251
|
+
|
252
|
+
def decode_text(data)
|
253
|
+
Base64.urlsafe_decode64 data rescue ''
|
254
|
+
end
|
255
|
+
|
256
|
+
def decode_embedding(embedding)
|
257
|
+
if embedding['mime_type'] =~ /^image\/(png|gif|jpg|jpeg)/
|
258
|
+
embedding['data'] = decode_image(embedding['data'])
|
259
|
+
elsif embedding['mime_type'] =~ /^text\/(plain|html)/
|
260
|
+
embedding['data'] = decode_text(embedding['data'])
|
261
|
+
end
|
262
|
+
embedding
|
263
|
+
end
|
264
|
+
|
265
|
+
def total_time(data)
|
266
|
+
total_time = 0
|
267
|
+
data.each {|item| total_time += item['duration']}
|
268
|
+
total_time
|
269
|
+
end
|
270
|
+
|
271
|
+
def duration(ms)
|
272
|
+
s = ms.to_f/1000000000
|
273
|
+
m, s = s.divmod(60)
|
274
|
+
if m > 59
|
275
|
+
h, m = m.divmod(60)
|
276
|
+
"#{h}h #{m}m #{'%.2f' % s}s"
|
277
|
+
elsif m > 0
|
278
|
+
"#{m}m #{'%.2f' % s}s"
|
279
|
+
else
|
280
|
+
"#{'%.3f' % s}s"
|
281
|
+
end
|
282
|
+
end
|
283
|
+
end
|
284
|
+
end
|