blinka-reporter 0.5.2 → 0.6.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 +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
|