blinka-reporter 0.5.2 → 0.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.github/dependabot.yml +18 -0
- data/.github/release.yml +9 -0
- data/.github/workflows/main.yml +46 -0
- data/.gitignore +28 -0
- data/.ruby-version +1 -0
- data/CHANGELOG.md +152 -0
- data/Gemfile +4 -0
- data/LICENSE +21 -0
- data/README.md +162 -0
- data/Rakefile +7 -0
- data/bin/blinka_reporter +7 -0
- data/bin/setup +15 -0
- data/blinka_reporter.gemspec +41 -0
- data/lib/blinka_reporter/blinka.rb +169 -0
- data/lib/blinka_reporter/cli.rb +36 -0
- data/lib/blinka_reporter/client.rb +109 -0
- data/lib/blinka_reporter/config.rb +34 -0
- data/lib/blinka_reporter/error.rb +3 -0
- data/lib/blinka_reporter/minitest_adapter.rb +92 -0
- data/lib/blinka_reporter/tap.rb +51 -0
- data/lib/blinka_reporter/version.rb +1 -1
- data/lib/blinka_reporter.rb +4 -0
- data/lib/minitest/blinka_plugin.rb +14 -59
- data/package.json +22 -0
- data/yarn.lock +481 -0
- metadata +41 -5
- data/lib/blinka_client.rb +0 -195
- data/lib/blinka_minitest.rb +0 -90
data/lib/blinka_client.rb
DELETED
@@ -1,195 +0,0 @@
|
|
1
|
-
require 'httparty'
|
2
|
-
|
3
|
-
class BlinkaClient
|
4
|
-
DEFAULT_HOST = 'https://www.blinka.app'.freeze
|
5
|
-
SUPPORTED_MIME_TYPES = {
|
6
|
-
jpg: 'image/jpeg',
|
7
|
-
jpeg: 'image/jpeg',
|
8
|
-
png: 'image/png'
|
9
|
-
}
|
10
|
-
|
11
|
-
include HTTParty
|
12
|
-
|
13
|
-
class BlinkaConfig
|
14
|
-
attr_reader(:host, :repository, :team_id, :team_secret, :jwt_token)
|
15
|
-
|
16
|
-
def initialize
|
17
|
-
@host = ENV.fetch('BLINKA_HOST', DEFAULT_HOST)
|
18
|
-
@team_id = ENV.fetch('BLINKA_TEAM_ID', nil)
|
19
|
-
@team_secret = ENV.fetch('BLINKA_TEAM_SECRET', nil)
|
20
|
-
@repository = ENV.fetch('BLINKA_REPOSITORY', nil)
|
21
|
-
|
22
|
-
if @team_id.nil? || @team_secret.nil? || @repository.nil?
|
23
|
-
raise(BlinkaError, <<~EOS)
|
24
|
-
Missing configuration, make sure to set required environment variables:
|
25
|
-
- BLINKA_TEAM_ID
|
26
|
-
- BLINKA_TEAM_SECRET
|
27
|
-
- BLINKA_REPOSITORY
|
28
|
-
EOS
|
29
|
-
end
|
30
|
-
end
|
31
|
-
end
|
32
|
-
|
33
|
-
class BlinkaError < StandardError; end
|
34
|
-
|
35
|
-
def initialize
|
36
|
-
@config = BlinkaConfig.new
|
37
|
-
self.class.base_uri("#{@config.host}/api/v1")
|
38
|
-
end
|
39
|
-
|
40
|
-
def report(filepath: './blinka_results.json')
|
41
|
-
unless File.exist?(filepath)
|
42
|
-
raise(
|
43
|
-
BlinkaError,
|
44
|
-
'Could not find blinka_results.json, did tests run with environment variable BLINKA_JSON=true set?'
|
45
|
-
)
|
46
|
-
end
|
47
|
-
|
48
|
-
if ENV.fetch('BLINKA_ALLOW_WEBMOCK_DISABLE', 'true') == 'true' &&
|
49
|
-
defined?(WebMock) && WebMock.respond_to?(:disable!)
|
50
|
-
WebMock.disable!
|
51
|
-
end
|
52
|
-
|
53
|
-
self.authenticate
|
54
|
-
data = JSON.parse(File.open(filepath).read)
|
55
|
-
|
56
|
-
results =
|
57
|
-
data
|
58
|
-
.fetch('results', [])
|
59
|
-
.map do |result|
|
60
|
-
if result.key?('image')
|
61
|
-
result['image'] =
|
62
|
-
BlinkaClient.upload_image(filepath: result['image'])
|
63
|
-
result
|
64
|
-
else
|
65
|
-
result
|
66
|
-
end
|
67
|
-
end
|
68
|
-
|
69
|
-
body = {
|
70
|
-
report: {
|
71
|
-
repository: @config.repository,
|
72
|
-
tag: data['tag'],
|
73
|
-
commit: data['commit'],
|
74
|
-
metadata: {
|
75
|
-
total_time: data['total_time'],
|
76
|
-
nbr_tests: data['nbr_tests'],
|
77
|
-
nbr_assertions: data['nbr_assertions'],
|
78
|
-
seed: data['seed']
|
79
|
-
}.compact,
|
80
|
-
results: results
|
81
|
-
}
|
82
|
-
}
|
83
|
-
|
84
|
-
response =
|
85
|
-
self.class.post(
|
86
|
-
'/report',
|
87
|
-
body: body.to_json,
|
88
|
-
headers: {
|
89
|
-
'Content-Type' => 'application/json',
|
90
|
-
'Authorization' => "Bearer #{@jwt_token}"
|
91
|
-
}
|
92
|
-
)
|
93
|
-
case response.code
|
94
|
-
when 200
|
95
|
-
puts "Reported #{data['nbr_tests']} tests of commit #{data['commit']}!"
|
96
|
-
else
|
97
|
-
raise(BlinkaError, "Could not report, got response code #{response.code}")
|
98
|
-
end
|
99
|
-
rescue => error
|
100
|
-
raise(BlinkaError, <<-EOS)
|
101
|
-
BLINKA:
|
102
|
-
Failed to create report because of #{error.class} with message:
|
103
|
-
#{error.message}
|
104
|
-
EOS
|
105
|
-
ensure
|
106
|
-
WebMock.enable! if defined?(WebMock) && WebMock.respond_to?(:enable!)
|
107
|
-
end
|
108
|
-
|
109
|
-
def self.report(filepath: './blinka_results.json')
|
110
|
-
client = BlinkaClient.new
|
111
|
-
client.report(filepath: filepath)
|
112
|
-
end
|
113
|
-
|
114
|
-
def self.upload_image(filepath:)
|
115
|
-
return unless File.exist?(filepath)
|
116
|
-
|
117
|
-
file = File.open(filepath)
|
118
|
-
filename = File.basename(filepath)
|
119
|
-
extension = File.extname(filepath).delete('.').to_sym
|
120
|
-
content_type = SUPPORTED_MIME_TYPES[extension]
|
121
|
-
return if content_type.nil?
|
122
|
-
|
123
|
-
presigned_post =
|
124
|
-
BlinkaClient.presign_image(filename: filename, content_type: content_type)
|
125
|
-
BlinkaClient.upload_to_storage(presigned_post: presigned_post, file: file)
|
126
|
-
|
127
|
-
puts "Uploaded: #{filename}"
|
128
|
-
BlinkaClient.to_shrine_object(
|
129
|
-
presigned_post: presigned_post,
|
130
|
-
file: file,
|
131
|
-
filename: filename
|
132
|
-
)
|
133
|
-
end
|
134
|
-
|
135
|
-
def authenticate
|
136
|
-
response =
|
137
|
-
self.class.post(
|
138
|
-
'/authentication',
|
139
|
-
body: {
|
140
|
-
token_id: @config.team_id,
|
141
|
-
token_secret: @config.team_secret
|
142
|
-
}.to_json,
|
143
|
-
headers: { 'Content-Type' => 'application/json' }
|
144
|
-
)
|
145
|
-
case response.code
|
146
|
-
when 200
|
147
|
-
@jwt_token = JSON.parse(response.body).dig('auth_token')
|
148
|
-
else
|
149
|
-
raise(BlinkaError, 'Could not authenticate to API')
|
150
|
-
end
|
151
|
-
end
|
152
|
-
|
153
|
-
def self.presign_image(filename:, content_type:)
|
154
|
-
response =
|
155
|
-
self.get(
|
156
|
-
'/presign',
|
157
|
-
body: { filename: filename, content_type: content_type }
|
158
|
-
)
|
159
|
-
|
160
|
-
case response.code
|
161
|
-
when 200
|
162
|
-
JSON.parse(response.body)
|
163
|
-
else
|
164
|
-
raise(BlinkaError, 'Could not presign file')
|
165
|
-
end
|
166
|
-
end
|
167
|
-
|
168
|
-
def self.upload_to_storage(presigned_post:, file:)
|
169
|
-
url = URI.parse(presigned_post.fetch('url'))
|
170
|
-
fields = presigned_post.fetch('fields')
|
171
|
-
|
172
|
-
body = presigned_post['fields'].merge({ 'file' => file.read })
|
173
|
-
response = HTTParty.post(url, multipart: true, body: body)
|
174
|
-
|
175
|
-
case response.code
|
176
|
-
when 204
|
177
|
-
true
|
178
|
-
else
|
179
|
-
raise(BlinkaError, 'Could not upload file to storage')
|
180
|
-
end
|
181
|
-
end
|
182
|
-
|
183
|
-
def self.to_shrine_object(presigned_post:, file:, filename:)
|
184
|
-
storage, idx = presigned_post.dig('fields', 'key').split('/')
|
185
|
-
{
|
186
|
-
"id": idx,
|
187
|
-
"storage": storage,
|
188
|
-
"metadata": {
|
189
|
-
"size": file.size,
|
190
|
-
"filename": filename,
|
191
|
-
"mime_type": presigned_post.dig('fields', 'Content-Type')
|
192
|
-
}
|
193
|
-
}
|
194
|
-
end
|
195
|
-
end
|
data/lib/blinka_minitest.rb
DELETED
@@ -1,90 +0,0 @@
|
|
1
|
-
class BlinkaMinitest
|
2
|
-
def initialize(test_result)
|
3
|
-
@test_result = test_result
|
4
|
-
end
|
5
|
-
|
6
|
-
def path
|
7
|
-
@path ||= source_location.first.gsub(Dir.getwd, '').delete_prefix('/')
|
8
|
-
end
|
9
|
-
|
10
|
-
def line
|
11
|
-
@line ||= source_location.last
|
12
|
-
end
|
13
|
-
|
14
|
-
# Handle broken API in Minitest between 5.10 and 5.11
|
15
|
-
# https://github.com/minitest-reporters/minitest-reporters/blob/e9092460b5a5cf5ca9eb375428217cbb2a7f6dbb/lib/minitest/reporters/default_reporter.rb#L159
|
16
|
-
def source_location
|
17
|
-
@source_location ||=
|
18
|
-
if @test_result.respond_to?(:klass)
|
19
|
-
@test_result.source_location
|
20
|
-
else
|
21
|
-
@test_result.method(@test_result.name).source_location
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
def kind
|
26
|
-
parts = self.path.gsub('test/', '').split('/')
|
27
|
-
parts.length > 1 ? parts.first : 'general'
|
28
|
-
end
|
29
|
-
|
30
|
-
def message
|
31
|
-
failure = @test_result.failure
|
32
|
-
return unless failure
|
33
|
-
"#{failure.error.class}: #{failure.error.message}"
|
34
|
-
end
|
35
|
-
|
36
|
-
def backtrace
|
37
|
-
return unless @test_result.failure
|
38
|
-
Minitest.filter_backtrace(@test_result.failure.backtrace)
|
39
|
-
end
|
40
|
-
|
41
|
-
def result
|
42
|
-
if @test_result.error?
|
43
|
-
:error
|
44
|
-
elsif @test_result.skipped?
|
45
|
-
:skip
|
46
|
-
elsif @test_result.failure
|
47
|
-
:fail
|
48
|
-
else
|
49
|
-
:pass
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
def time
|
54
|
-
@test_result.time
|
55
|
-
end
|
56
|
-
|
57
|
-
def name
|
58
|
-
@test_result.name
|
59
|
-
end
|
60
|
-
|
61
|
-
def image
|
62
|
-
return unless kind == 'system'
|
63
|
-
|
64
|
-
image_path =
|
65
|
-
if defined?(Capybara) && Capybara.respond_to?(:save_path) &&
|
66
|
-
Capybara.save_path.present?
|
67
|
-
"#{Capybara.save_path}/failures_#{name}.png"
|
68
|
-
else
|
69
|
-
"./tmp/screenshots/failures_#{name}.png"
|
70
|
-
end
|
71
|
-
|
72
|
-
return unless File.exist?(image_path)
|
73
|
-
|
74
|
-
image_path
|
75
|
-
end
|
76
|
-
|
77
|
-
def report
|
78
|
-
{
|
79
|
-
backtrace: backtrace,
|
80
|
-
message: message,
|
81
|
-
line: line,
|
82
|
-
image: image,
|
83
|
-
kind: kind,
|
84
|
-
name: name,
|
85
|
-
path: path,
|
86
|
-
result: result,
|
87
|
-
time: time
|
88
|
-
}.compact
|
89
|
-
end
|
90
|
-
end
|