blinka-reporter 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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: []