report_builder_fix 0.2 → 0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/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
|