capydash 0.1.7 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: a911afa2a05f90b508d27c730cf9762f1f603669e6ff4005aba83187168de794
4
- data.tar.gz: f6bb688a4b23dac1550d238bac1e7bb8fffa6e9e9a59d95d839763500d9556d1
3
+ metadata.gz: '08b9f8d42cd5b408688aaffd4e8000a2f2514e1b7cababeaef8e6b5f22e6255b'
4
+ data.tar.gz: fe000167bd555af57a386e7616a4a475b577881b8d8c7cd2cec996e81d5ce088
5
5
  SHA512:
6
- metadata.gz: f700f19aa73d7affb6b0606193bf8ed3cda38eda6048ca9c9efdef67d5512bc0529a2eda834707bccbbf695a85524047707125acfe89bf941f20e722c216c430
7
- data.tar.gz: b20c29fbfb9146eb2937034fad10351c0459c03c9ef91bd5b544c7847f03e9c93f657877a9b12d8cb0b8380368a1894d25c7777f4c2a4550866a64d74e66b8df
6
+ metadata.gz: 25087505b55812541a3be9184467174c205c4c6b8cc75bd6c0c74c5c9a8995cbcd19c1f1b160c5ebda02e4cf14a6adb26de2639bc1097724931182e4ff9bbf2b
7
+ data.tar.gz: 16ebd46b3f89a641cd0260dbb2f08f7702b59ea89478568fbeb06e3f47e5e6a09dfdf501dd5cb528399d4dbed24417fd42c286dce391e625df9aff774168c4a0
@@ -0,0 +1,285 @@
1
+ require 'time'
2
+ require 'securerandom'
3
+ require 'fileutils'
4
+ require 'erb'
5
+
6
+ module CapyDash
7
+ module RSpecIntegration
8
+ class << self
9
+ def setup!
10
+ return unless defined?(RSpec)
11
+ return if @configured
12
+
13
+ @results = []
14
+ @run_id = nil
15
+ @configured = true
16
+
17
+ RSpec.configure do |config|
18
+ config.before(:suite) do
19
+ CapyDash::RSpecIntegration.start_test_run
20
+ end
21
+
22
+ config.after(:each) do |example|
23
+ CapyDash::RSpecIntegration.record_example(example)
24
+ end
25
+
26
+ config.after(:suite) do
27
+ CapyDash::RSpecIntegration.finish_test_run
28
+ end
29
+ end
30
+ end
31
+
32
+ def start_test_run
33
+ @run_id = generate_run_id
34
+ @results = []
35
+ @started_at = Time.now
36
+ end
37
+
38
+ def record_example(example)
39
+ return unless @run_id
40
+
41
+ execution_result = example.execution_result
42
+
43
+ # Map RSpec status to our status format
44
+ status = case execution_result.status.to_s
45
+ when 'passed'
46
+ 'passed'
47
+ when 'failed'
48
+ 'failed'
49
+ when 'pending'
50
+ 'pending'
51
+ else
52
+ 'unknown'
53
+ end
54
+
55
+ # Extract error message if test failed
56
+ error_message = nil
57
+ if execution_result.status == :failed && execution_result.exception
58
+ error_message = format_exception(execution_result.exception)
59
+ end
60
+
61
+ # Extract class name from example location
62
+ # RSpec examples are typically in files like spec/features/user_spec.rb
63
+ # We'll use the file path to determine the "class" name
64
+ file_path = example.metadata[:file_path] || ''
65
+ class_name = extract_class_name_from_path(file_path)
66
+
67
+ # Create test data structure matching Minitest format
68
+ test_data = {
69
+ test_name: "#{class_name}##{example.full_description}",
70
+ steps: [
71
+ {
72
+ step_name: 'test_execution',
73
+ detail: example.full_description,
74
+ status: status,
75
+ error: error_message
76
+ }
77
+ ]
78
+ }
79
+
80
+ # Add location information
81
+ if example.metadata[:location]
82
+ test_data[:location] = example.metadata[:location]
83
+ end
84
+
85
+ @results << test_data
86
+ end
87
+
88
+ def finish_test_run
89
+ return unless @run_id
90
+ return if @results.empty?
91
+
92
+ # Calculate summary statistics
93
+ total_tests = @results.length
94
+ passed_tests = @results.count { |r| r[:steps].any? { |s| s[:status] == 'passed' } }
95
+ failed_tests = @results.count { |r| r[:steps].any? { |s| s[:status] == 'failed' } }
96
+ pending_tests = @results.count { |r| r[:steps].any? { |s| s[:status] == 'pending' } }
97
+
98
+ # Create run data structure matching Minitest format
99
+ run_data = {
100
+ id: @run_id,
101
+ created_at: @started_at.iso8601,
102
+ total_tests: total_tests,
103
+ passed_tests: passed_tests,
104
+ failed_tests: failed_tests,
105
+ tests: @results.map { |r| { test_name: r[:test_name], steps: r[:steps] } }
106
+ }
107
+
108
+ # Save using the existing persistence layer
109
+ CapyDash.save_test_run(run_data)
110
+
111
+ # Generate report
112
+ generate_report(run_data)
113
+
114
+ # Clear state
115
+ @run_id = nil
116
+ @results = []
117
+ end
118
+
119
+ private
120
+
121
+ def generate_run_id
122
+ "#{Time.now.to_i}_#{SecureRandom.hex(4)}"
123
+ end
124
+
125
+ def extract_class_name_from_path(file_path)
126
+ return 'UnknownSpec' if file_path.nil? || file_path.empty?
127
+
128
+ # Extract filename without extension and path
129
+ filename = File.basename(file_path, '.rb')
130
+
131
+ # Convert snake_case to PascalCase
132
+ # e.g., "user_spec" -> "UserSpec", "features/user_flow_spec" -> "UserFlowSpec"
133
+ filename.split('_').map(&:capitalize).join('')
134
+ end
135
+
136
+ def format_exception(exception)
137
+ return nil unless exception
138
+
139
+ message = exception.message || 'Unknown error'
140
+ backtrace = exception.backtrace || []
141
+
142
+ # Format similar to RSpec's output
143
+ formatted = "#{exception.class}: #{message}"
144
+
145
+ if backtrace.any?
146
+ # Include first few lines of backtrace
147
+ formatted += "\n" + backtrace.first(5).map { |line| " #{line}" }.join("\n")
148
+ end
149
+
150
+ formatted
151
+ end
152
+
153
+ def generate_report(run_data)
154
+ # Use the existing ReportGenerator but with our RSpec data
155
+ # We need to adapt it to work with our in-memory data structure
156
+ report_dir = File.join(Dir.pwd, "capydash_report")
157
+ FileUtils.mkdir_p(report_dir)
158
+
159
+ assets_dir = File.join(report_dir, "assets")
160
+ FileUtils.mkdir_p(assets_dir)
161
+
162
+ screenshots_dir = File.join(report_dir, "screenshots")
163
+ FileUtils.mkdir_p(screenshots_dir)
164
+
165
+ # Generate HTML report using the same template
166
+ html_content = generate_html(run_data, run_data[:created_at])
167
+ html_path = File.join(report_dir, "index.html")
168
+ File.write(html_path, html_content)
169
+
170
+ # Generate CSS and JS - use ReportGenerator's private methods via send
171
+ # These methods are private but we need them for RSpec reports
172
+ css_content = CapyDash::ReportGenerator.send(:generate_css)
173
+ css_path = File.join(assets_dir, "dashboard.css")
174
+ File.write(css_path, css_content)
175
+
176
+ js_content = CapyDash::ReportGenerator.send(:generate_javascript)
177
+ js_path = File.join(assets_dir, "dashboard.js")
178
+ File.write(js_path, js_content)
179
+
180
+ html_path
181
+ end
182
+
183
+ def generate_html(test_data, created_at)
184
+ # Process test data into a structured format (same as ReportGenerator)
185
+ processed_tests = process_test_data(test_data)
186
+
187
+ # Calculate summary statistics
188
+ total_tests = processed_tests.sum { |test| test[:methods].length }
189
+ passed_tests = processed_tests.sum { |test| test[:methods].count { |method| method[:status] == 'passed' } }
190
+ failed_tests = total_tests - passed_tests
191
+
192
+ # Parse created_at if it's a string, otherwise use Time object
193
+ created_at_time = if created_at.is_a?(String)
194
+ Time.parse(created_at)
195
+ else
196
+ created_at
197
+ end
198
+
199
+ # Generate HTML using ERB template
200
+ template = File.read(File.join(__dir__, 'templates', 'report.html.erb'))
201
+ erb = ERB.new(template)
202
+
203
+ erb.result(binding)
204
+ end
205
+
206
+ def process_test_data(test_data)
207
+ return [] unless test_data[:tests]
208
+
209
+ # Group tests by class
210
+ tests_by_class = {}
211
+
212
+ test_data[:tests].each do |test|
213
+ test_name = test[:test_name] || 'UnknownTest'
214
+
215
+ # Extract class and method names from test name like "UserSpec#should visit the home page"
216
+ if test_name.include?('#')
217
+ class_name, method_name = test_name.split('#', 2)
218
+ else
219
+ class_name = extract_class_name(test_name)
220
+ method_name = extract_method_name(test_name)
221
+ end
222
+
223
+ tests_by_class[class_name] ||= {
224
+ class_name: class_name,
225
+ methods: []
226
+ }
227
+
228
+ # Process steps
229
+ steps = test[:steps] || []
230
+ processed_steps = steps.map do |step|
231
+ {
232
+ name: step[:step_name] || step[:name] || 'unknown_step',
233
+ detail: step[:detail] || step[:description] || '',
234
+ status: step[:status] || 'unknown',
235
+ screenshot: step[:screenshot] ? File.basename(step[:screenshot]) : nil,
236
+ error: step[:error] || step[:message]
237
+ }
238
+ end
239
+
240
+ # Filter out "running" steps
241
+ processed_steps = processed_steps.reject { |step| step[:status] == 'running' }
242
+
243
+ # Determine method status
244
+ method_status = if processed_steps.any? { |s| s[:status] == 'failed' }
245
+ 'failed'
246
+ elsif processed_steps.any? { |s| s[:status] == 'passed' }
247
+ 'passed'
248
+ elsif processed_steps.any? { |s| s[:status] == 'pending' }
249
+ 'pending'
250
+ else
251
+ 'running'
252
+ end
253
+
254
+ tests_by_class[class_name][:methods] << {
255
+ name: method_name,
256
+ status: method_status,
257
+ steps: processed_steps
258
+ }
259
+ end
260
+
261
+ tests_by_class.values
262
+ end
263
+
264
+ def extract_class_name(test_name)
265
+ return 'UnknownTest' if test_name.nil? || test_name.empty?
266
+
267
+ if test_name.include?('#')
268
+ test_name.split('#').first
269
+ else
270
+ test_name
271
+ end
272
+ end
273
+
274
+ def extract_method_name(test_name)
275
+ return 'unknown_method' if test_name.nil? || test_name.empty?
276
+
277
+ if test_name.include?('#')
278
+ test_name.split('#').last
279
+ else
280
+ test_name
281
+ end
282
+ end
283
+ end
284
+ end
285
+ end
@@ -1,3 +1,3 @@
1
1
  module Capydash
2
- VERSION = "0.1.7"
2
+ VERSION = "0.2.0"
3
3
  end
data/lib/capydash.rb CHANGED
@@ -13,6 +13,12 @@ require "capydash/test_data_collector"
13
13
  require "capydash/test_data_aggregator"
14
14
  require "capydash/report_generator"
15
15
 
16
+ # Conditionally load RSpec integration if RSpec is present
17
+ if defined?(RSpec)
18
+ require "capydash/rspec_integration"
19
+ CapyDash::RSpecIntegration.setup!
20
+ end
21
+
16
22
  module CapyDash
17
23
  class << self
18
24
  attr_accessor :configuration, :current_test, :config
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: capydash
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.7
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Damon Clark
@@ -129,6 +129,7 @@ files:
129
129
  - lib/capydash/logger.rb
130
130
  - lib/capydash/persistence.rb
131
131
  - lib/capydash/report_generator.rb
132
+ - lib/capydash/rspec_integration.rb
132
133
  - lib/capydash/templates/report.html.erb
133
134
  - lib/capydash/test_data_aggregator.rb
134
135
  - lib/capydash/test_data_collector.rb