blinka-reporter 0.0.1
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 +7 -0
- data/lib/blinka_client.rb +161 -0
- data/lib/blinka_minitest.rb +92 -0
- data/lib/minitest/blinka_plugin.rb +46 -0
- metadata +129 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 1f41cbd0de7d1ac7f76f2c6851c15601b96b62ff4eae50ef87bf6a7c6cd17a71
|
4
|
+
data.tar.gz: 121c8bd87cb7ee1774d174c1e38c1985d902f0f84e9301b5139f99c5dee056c6
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: b5c19402bd82d52180918458f1718d04963af0e091009443d60e018b21f46a841299313d613c10d209e6e126e9a2d50e22795302d4945f7b245f1b5cbf390b1b
|
7
|
+
data.tar.gz: 593d9f154261a39cee153067e4a4548a2fac34b8021aa75f1fe89039201e6ce928031892062e7103a2f86dfe31757e4a4a37175b2f17b5604a69175ccbafbeb4
|
@@ -0,0 +1,161 @@
|
|
1
|
+
require 'mimemagic'
|
2
|
+
require 'httparty'
|
3
|
+
|
4
|
+
class BlinkaClient
|
5
|
+
class BlinkaConfig
|
6
|
+
attr_reader(
|
7
|
+
:branch,
|
8
|
+
:commit,
|
9
|
+
:host,
|
10
|
+
:repository,
|
11
|
+
:team_id,
|
12
|
+
:team_secret,
|
13
|
+
:jwt_token
|
14
|
+
)
|
15
|
+
def initialize
|
16
|
+
@host = ENV.fetch('BLINKA_HOST', 'https://blinkblink.herokuapp.com')
|
17
|
+
@team_id = ENV.fetch('BLINKA_TEAM_ID')
|
18
|
+
@team_secret = ENV.fetch('BLINKA_TEAM_SECRET')
|
19
|
+
@repository = ENV.fetch('BLINKA_REPOSITORY')
|
20
|
+
@branch = ENV.fetch('BLINKA_BRANCH')
|
21
|
+
@commit = `git rev-parse HEAD`.chomp
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
class BlinkaError < StandardError; end
|
26
|
+
include HTTParty
|
27
|
+
|
28
|
+
def initialize
|
29
|
+
@config = BlinkaConfig.new
|
30
|
+
self.class.base_uri("#{@config.host}/api/v1")
|
31
|
+
end
|
32
|
+
|
33
|
+
def report(filepath: './blinka_results.json')
|
34
|
+
self.authenticate
|
35
|
+
data = JSON.parse(File.open(filepath).read)
|
36
|
+
|
37
|
+
results =
|
38
|
+
data
|
39
|
+
.fetch('results', [])
|
40
|
+
.map do |result|
|
41
|
+
if result.key?('image')
|
42
|
+
result['image'] =
|
43
|
+
BlinkaClient.upload_image(filepath: result['image'])
|
44
|
+
result
|
45
|
+
else
|
46
|
+
result
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
body = {
|
51
|
+
report: {
|
52
|
+
repository: @config.repository,
|
53
|
+
branch: @config.branch,
|
54
|
+
commit: @config.commit,
|
55
|
+
metadata: {
|
56
|
+
total_time: data.dig('total_time'),
|
57
|
+
nbr_tests: data.dig('nbr_tests'),
|
58
|
+
nbr_assertions: data.dig('nbr_assertions'),
|
59
|
+
seed: data.dig('seed')
|
60
|
+
}.compact,
|
61
|
+
results: results
|
62
|
+
}
|
63
|
+
}
|
64
|
+
|
65
|
+
response =
|
66
|
+
self.class.post(
|
67
|
+
'/report',
|
68
|
+
body: body.to_json,
|
69
|
+
headers: {
|
70
|
+
'Content-Type' => 'application/json',
|
71
|
+
'Authorization' => "Bearer #{@jwt_token}"
|
72
|
+
}
|
73
|
+
)
|
74
|
+
case response.code
|
75
|
+
when 200
|
76
|
+
puts "Reported #{data.dig('nbr_tests')} tests!"
|
77
|
+
else
|
78
|
+
raise(BlinkaError, "Could not report, got response code #{response.code}")
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
def self.upload_image(filepath:)
|
83
|
+
return unless File.exist?(filepath)
|
84
|
+
|
85
|
+
file = File.open(filepath)
|
86
|
+
filename = File.basename(filepath)
|
87
|
+
content_type = MimeMagic.by_magic(file).type
|
88
|
+
|
89
|
+
presigned_post =
|
90
|
+
BlinkaClient.presign_image(filename: filename, content_type: content_type)
|
91
|
+
BlinkaClient.upload_to_storage(presigned_post: presigned_post, file: file)
|
92
|
+
|
93
|
+
puts "Uploaded: #{filename}"
|
94
|
+
BlinkaClient.to_shrine_object(
|
95
|
+
presigned_post: presigned_post,
|
96
|
+
file: file,
|
97
|
+
filename: filename
|
98
|
+
)
|
99
|
+
end
|
100
|
+
|
101
|
+
def authenticate
|
102
|
+
response =
|
103
|
+
self.class.post(
|
104
|
+
'/authentication',
|
105
|
+
body: {
|
106
|
+
token_id: @config.team_id,
|
107
|
+
token_secret: @config.team_secret
|
108
|
+
}.to_json,
|
109
|
+
headers: { 'Content-Type' => 'application/json' }
|
110
|
+
)
|
111
|
+
case response.code
|
112
|
+
when 200
|
113
|
+
@jwt_token = JSON.parse(response.body).dig('auth_token')
|
114
|
+
else
|
115
|
+
raise(BlinkaError, 'Could not authenticate to API')
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
def self.presign_image(filename:, content_type:)
|
120
|
+
response =
|
121
|
+
self.get(
|
122
|
+
'/presign',
|
123
|
+
body: { filename: filename, content_type: content_type }
|
124
|
+
)
|
125
|
+
|
126
|
+
case response.code
|
127
|
+
when 200
|
128
|
+
JSON.parse(response.body)
|
129
|
+
else
|
130
|
+
raise(BlinkaError, 'Could not presign file')
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
def self.upload_to_storage(presigned_post:, file:)
|
135
|
+
url = URI.parse(presigned_post.fetch('url'))
|
136
|
+
fields = presigned_post.fetch('fields')
|
137
|
+
|
138
|
+
body = presigned_post['fields'].merge({ 'file' => file.read })
|
139
|
+
response = HTTParty.post(url, multipart: true, body: body)
|
140
|
+
|
141
|
+
case response.code
|
142
|
+
when 204
|
143
|
+
true
|
144
|
+
else
|
145
|
+
raise(BlinkaError, 'Could not upload file to storage')
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
def self.to_shrine_object(presigned_post:, file:, filename:)
|
150
|
+
storage, idx = presigned_post.dig('fields', 'key').split('/')
|
151
|
+
{
|
152
|
+
"id": idx,
|
153
|
+
"storage": storage,
|
154
|
+
"metadata": {
|
155
|
+
"size": file.size,
|
156
|
+
"filename": filename,
|
157
|
+
"mime_type": presigned_post.dig('fields', 'Content-Type')
|
158
|
+
}
|
159
|
+
}
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
class BlinkaMinitest
|
2
|
+
attr_reader(:test_result)
|
3
|
+
def initialize(test_result)
|
4
|
+
@test_result = test_result
|
5
|
+
end
|
6
|
+
|
7
|
+
def path
|
8
|
+
@path ||= source_location.first.gsub(Dir.getwd, '').delete_prefix('/')
|
9
|
+
end
|
10
|
+
|
11
|
+
# Handle broken API in Minitest between 5.10 and 5.11
|
12
|
+
# https://github.com/minitest-reporters/minitest-reporters/blob/e9092460b5a5cf5ca9eb375428217cbb2a7f6dbb/lib/minitest/reporters/default_reporter.rb#L159
|
13
|
+
def source_location
|
14
|
+
if test_result.respond_to?(:klass)
|
15
|
+
test_result.source_location
|
16
|
+
else
|
17
|
+
test_result.method(test_result.name).source_location
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def kind
|
22
|
+
parts = self.path.gsub('test/', '').split('/')
|
23
|
+
parts.length > 1 ? parts.first : 'general'
|
24
|
+
end
|
25
|
+
|
26
|
+
def message
|
27
|
+
failure = test_result.failure
|
28
|
+
return unless failure
|
29
|
+
"#{failure.error.class}: #{failure.error.message}"
|
30
|
+
end
|
31
|
+
|
32
|
+
def backtrace
|
33
|
+
return unless test_result.failure
|
34
|
+
Minitest.filter_backtrace(test_result.failure.backtrace)
|
35
|
+
end
|
36
|
+
|
37
|
+
def result
|
38
|
+
if test_result.error?
|
39
|
+
:error
|
40
|
+
elsif test_result.skipped?
|
41
|
+
:skip
|
42
|
+
elsif test_result.failure
|
43
|
+
:failed
|
44
|
+
else
|
45
|
+
:pass
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def line
|
50
|
+
current_backtrace = backtrace
|
51
|
+
return if current_backtrace.nil?
|
52
|
+
|
53
|
+
row =
|
54
|
+
current_backtrace
|
55
|
+
.map { |row| row.split(':')[0..1] }
|
56
|
+
.detect { |row| row[0] == path }
|
57
|
+
|
58
|
+
return if row.nil?
|
59
|
+
row[1].to_i
|
60
|
+
end
|
61
|
+
|
62
|
+
def time
|
63
|
+
test_result.time
|
64
|
+
end
|
65
|
+
|
66
|
+
def name
|
67
|
+
test_result.name
|
68
|
+
end
|
69
|
+
|
70
|
+
def image
|
71
|
+
return unless kind == 'system'
|
72
|
+
|
73
|
+
image_path = "./tmp/screenshots/failures_#{name}.png"
|
74
|
+
return unless File.exist?(image_path)
|
75
|
+
|
76
|
+
image_path
|
77
|
+
end
|
78
|
+
|
79
|
+
def report
|
80
|
+
{
|
81
|
+
backtrace: backtrace,
|
82
|
+
message: message,
|
83
|
+
line: line,
|
84
|
+
image: image,
|
85
|
+
kind: kind,
|
86
|
+
name: name,
|
87
|
+
path: path,
|
88
|
+
result: result,
|
89
|
+
time: time
|
90
|
+
}.compact
|
91
|
+
end
|
92
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'minitest'
|
2
|
+
require 'json'
|
3
|
+
require 'blinka_minitest'
|
4
|
+
|
5
|
+
module Minitest
|
6
|
+
def self.plugin_blinka_init(options)
|
7
|
+
reporter.reporters << BlinkaPlugin::Reporter.new(options[:io], options)
|
8
|
+
end
|
9
|
+
|
10
|
+
def plugin_blinka_options(opts, options); end
|
11
|
+
|
12
|
+
module BlinkaPlugin
|
13
|
+
class Reporter < Minitest::StatisticsReporter
|
14
|
+
attr_accessor :tests
|
15
|
+
|
16
|
+
def initialize(io = $stdout, options = {})
|
17
|
+
super
|
18
|
+
self.tests = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def record(test)
|
22
|
+
super
|
23
|
+
tests << test
|
24
|
+
end
|
25
|
+
|
26
|
+
def report
|
27
|
+
super
|
28
|
+
|
29
|
+
result = {
|
30
|
+
total_time: total_time,
|
31
|
+
nbr_tests: count,
|
32
|
+
nbr_assertions: assertions,
|
33
|
+
seed: options[:seed],
|
34
|
+
results:
|
35
|
+
tests.map { |test_result| BlinkaMinitest.new(test_result).report }
|
36
|
+
}
|
37
|
+
|
38
|
+
File.open('blinka_results.json', 'w+') do |file|
|
39
|
+
file.write(JSON.pretty_generate(result))
|
40
|
+
end
|
41
|
+
puts
|
42
|
+
puts('Test results written to `./blinka_results.json`')
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
metadata
ADDED
@@ -0,0 +1,129 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: blinka-reporter
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- David Wessman
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2021-01-31 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: httparty
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.18.1
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.18.1
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: mimemagic
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 0.3.5
|
34
|
+
type: :runtime
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: 0.3.5
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: dotenv
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: 2.7.6
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: 2.7.6
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: minitest
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '5.0'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '5.0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: mocha
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.12'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.12'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: rake
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '13'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '13'
|
97
|
+
description: Use to format test results from Minitest to use with Blinka.
|
98
|
+
email: david@wessman.co
|
99
|
+
executables: []
|
100
|
+
extensions: []
|
101
|
+
extra_rdoc_files: []
|
102
|
+
files:
|
103
|
+
- lib/blinka_client.rb
|
104
|
+
- lib/blinka_minitest.rb
|
105
|
+
- lib/minitest/blinka_plugin.rb
|
106
|
+
homepage: https://rubygemspec.org/gems/blinka-reporter
|
107
|
+
licenses:
|
108
|
+
- MIT
|
109
|
+
metadata: {}
|
110
|
+
post_install_message:
|
111
|
+
rdoc_options: []
|
112
|
+
require_paths:
|
113
|
+
- lib
|
114
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
115
|
+
requirements:
|
116
|
+
- - ">="
|
117
|
+
- !ruby/object:Gem::Version
|
118
|
+
version: '0'
|
119
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
120
|
+
requirements:
|
121
|
+
- - ">="
|
122
|
+
- !ruby/object:Gem::Version
|
123
|
+
version: '0'
|
124
|
+
requirements: []
|
125
|
+
rubygems_version: 3.1.2
|
126
|
+
signing_key:
|
127
|
+
specification_version: 4
|
128
|
+
summary: Format tests for Blinka
|
129
|
+
test_files: []
|