capydash 0.1.1 → 0.1.3
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/README.md +35 -15
- data/lib/capydash/dashboard_server.rb +0 -8
- data/lib/capydash/engine.rb +0 -2
- data/lib/capydash/forwarder.rb +0 -6
- data/lib/capydash/instrumentation.rb +64 -29
- data/lib/capydash/persistence.rb +0 -5
- data/lib/capydash/report_generator.rb +1007 -0
- data/lib/capydash/templates/report.html.erb +92 -0
- data/lib/capydash/test_data_aggregator.rb +128 -0
- data/lib/capydash/test_data_collector.rb +0 -18
- data/lib/capydash/version.rb +1 -1
- data/lib/capydash.rb +2 -0
- data/lib/tasks/capydash.rake +62 -6
- metadata +4 -1
@@ -0,0 +1,92 @@
|
|
1
|
+
<!DOCTYPE html>
|
2
|
+
<html lang="en">
|
3
|
+
<head>
|
4
|
+
<meta charset="UTF-8">
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
6
|
+
<title>CapyDash - Test Dashboard Report</title>
|
7
|
+
<link rel="stylesheet" href="assets/dashboard.css">
|
8
|
+
</head>
|
9
|
+
<body>
|
10
|
+
<div class="container">
|
11
|
+
<div class="header">
|
12
|
+
<h1>CapyDash Test Report</h1>
|
13
|
+
<div class="subtitle">Generated on <%= created_at.strftime('%B %d, %Y at %I:%M %p') %></div>
|
14
|
+
|
15
|
+
<div class="search-container">
|
16
|
+
<div class="search-input-wrapper">
|
17
|
+
<input type="text" id="searchInput" placeholder="Search tests, steps, or errors..." class="search-input" autocomplete="off">
|
18
|
+
<div id="typeaheadDropdown" class="typeahead-dropdown"></div>
|
19
|
+
</div>
|
20
|
+
<div id="searchStats" class="search-stats"></div>
|
21
|
+
</div>
|
22
|
+
</div>
|
23
|
+
|
24
|
+
<div class="summary">
|
25
|
+
<div class="summary-card total">
|
26
|
+
<div class="number"><%= total_tests %></div>
|
27
|
+
<div class="label">Total Tests</div>
|
28
|
+
</div>
|
29
|
+
<div class="summary-card passed">
|
30
|
+
<div class="number"><%= passed_tests %></div>
|
31
|
+
<div class="label">Passed</div>
|
32
|
+
</div>
|
33
|
+
<div class="summary-card failed">
|
34
|
+
<div class="number"><%= failed_tests %></div>
|
35
|
+
<div class="label">Failed</div>
|
36
|
+
</div>
|
37
|
+
</div>
|
38
|
+
|
39
|
+
<div class="test-results">
|
40
|
+
<% processed_tests.each do |test_class| %>
|
41
|
+
<div class="test-class">
|
42
|
+
<h2><%= test_class[:class_name] %></h2>
|
43
|
+
|
44
|
+
<% test_class[:methods].each do |method| %>
|
45
|
+
<div class="test-method">
|
46
|
+
<div class="test-method-header">
|
47
|
+
<button class="expand-toggle" onclick="toggleTestMethod('<%= method[:name] %>')">
|
48
|
+
<span class="expand-icon">▶</span>
|
49
|
+
</button>
|
50
|
+
<h3><%= method[:name] %></h3>
|
51
|
+
</div>
|
52
|
+
<div class="steps collapsed" id="steps-<%= method[:name] %>">
|
53
|
+
<% method[:steps].each_with_index do |step, step_index| %>
|
54
|
+
<div class="step <%= step[:status] %>">
|
55
|
+
<div class="step-header">
|
56
|
+
<span class="step-name"><%= step[:name] %></span>
|
57
|
+
<span class="step-status"><%= step[:status] %></span>
|
58
|
+
|
59
|
+
<% if step[:screenshot] %>
|
60
|
+
<button class="screenshot-toggle" onclick="toggleScreenshot('<%= method[:name] %>-<%= step_index %>')">
|
61
|
+
📸 Screenshot
|
62
|
+
</button>
|
63
|
+
<% end %>
|
64
|
+
</div>
|
65
|
+
<div class="step-detail"><%= step[:detail] %></div>
|
66
|
+
|
67
|
+
<% if step[:error] %>
|
68
|
+
<div class="error-log">
|
69
|
+
<h4>Error Details</h4>
|
70
|
+
<pre><%= step[:error] %></pre>
|
71
|
+
</div>
|
72
|
+
<% end %>
|
73
|
+
|
74
|
+
<% if step[:screenshot] %>
|
75
|
+
<div class="screenshot-container" id="screenshot-<%= method[:name] %>-<%= step_index %>" style="display: none;">
|
76
|
+
<div class="screenshot">
|
77
|
+
<img src="screenshots/<%= step[:screenshot] %>" alt="Screenshot">
|
78
|
+
</div>
|
79
|
+
</div>
|
80
|
+
<% end %>
|
81
|
+
</div>
|
82
|
+
<% end %>
|
83
|
+
</div>
|
84
|
+
</div>
|
85
|
+
<% end %>
|
86
|
+
</div>
|
87
|
+
<% end %>
|
88
|
+
</div>
|
89
|
+
</div>
|
90
|
+
<script src="assets/dashboard.js"></script>
|
91
|
+
</body>
|
92
|
+
</html>
|
@@ -0,0 +1,128 @@
|
|
1
|
+
require 'json'
|
2
|
+
require 'time'
|
3
|
+
require 'securerandom'
|
4
|
+
|
5
|
+
module CapyDash
|
6
|
+
class TestDataAggregator
|
7
|
+
class << self
|
8
|
+
def start_test_run
|
9
|
+
@current_run = {
|
10
|
+
id: generate_run_id,
|
11
|
+
created_at: Time.now.iso8601,
|
12
|
+
total_tests: 0,
|
13
|
+
passed_tests: 0,
|
14
|
+
failed_tests: 0,
|
15
|
+
tests: []
|
16
|
+
}
|
17
|
+
@current_test = nil
|
18
|
+
@test_steps = []
|
19
|
+
end
|
20
|
+
|
21
|
+
def finish_test_run
|
22
|
+
return unless @current_run
|
23
|
+
|
24
|
+
# Save the test run data
|
25
|
+
CapyDash.save_test_run(@current_run)
|
26
|
+
|
27
|
+
# Clear current state
|
28
|
+
@current_run = nil
|
29
|
+
@current_test = nil
|
30
|
+
@test_steps = nil
|
31
|
+
end
|
32
|
+
|
33
|
+
def handle_event(event)
|
34
|
+
return unless @current_run
|
35
|
+
|
36
|
+
case event[:step_name]
|
37
|
+
when 'test_start'
|
38
|
+
start_new_test(event)
|
39
|
+
when 'test_finish'
|
40
|
+
finish_current_test(event)
|
41
|
+
when 'test_result'
|
42
|
+
# This indicates the test is finished
|
43
|
+
finish_current_test(event)
|
44
|
+
else
|
45
|
+
# This is a test step (visit, click_button, fill_in, etc.)
|
46
|
+
# If we don't have a current test, start one
|
47
|
+
start_new_test(event) unless @current_test
|
48
|
+
add_test_step(event)
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
def start_new_test(event)
|
55
|
+
# Extract test name from the current test context
|
56
|
+
test_name = event[:test_name] || CapyDash.current_test || "unknown_test"
|
57
|
+
|
58
|
+
@current_test = {
|
59
|
+
test_name: test_name,
|
60
|
+
steps: []
|
61
|
+
}
|
62
|
+
@test_steps = []
|
63
|
+
|
64
|
+
# Add the test_start step
|
65
|
+
add_test_step(event)
|
66
|
+
end
|
67
|
+
|
68
|
+
def finish_current_test(event)
|
69
|
+
return unless @current_test
|
70
|
+
|
71
|
+
# Add the test_finish step
|
72
|
+
add_test_step(event)
|
73
|
+
|
74
|
+
# Determine test status
|
75
|
+
test_status = determine_test_status(@test_steps)
|
76
|
+
|
77
|
+
# Update counters
|
78
|
+
@current_run[:total_tests] += 1
|
79
|
+
if test_status == 'passed'
|
80
|
+
@current_run[:passed_tests] += 1
|
81
|
+
elsif test_status == 'failed'
|
82
|
+
@current_run[:failed_tests] += 1
|
83
|
+
end
|
84
|
+
|
85
|
+
# Add test to current run
|
86
|
+
@current_run[:tests] << @current_test
|
87
|
+
|
88
|
+
# Clear current test
|
89
|
+
@current_test = nil
|
90
|
+
@test_steps = nil
|
91
|
+
end
|
92
|
+
|
93
|
+
def add_test_step(event)
|
94
|
+
return unless @current_test
|
95
|
+
|
96
|
+
step = {
|
97
|
+
step_name: event[:step_name],
|
98
|
+
detail: event[:detail],
|
99
|
+
status: event[:status],
|
100
|
+
test_name: event[:test_name] || CapyDash.current_test || @current_test[:test_name]
|
101
|
+
}
|
102
|
+
|
103
|
+
# Add screenshot if present
|
104
|
+
if event[:screenshot]
|
105
|
+
step[:screenshot] = event[:screenshot]
|
106
|
+
end
|
107
|
+
|
108
|
+
# Add error if present
|
109
|
+
if event[:error]
|
110
|
+
step[:error] = event[:error]
|
111
|
+
end
|
112
|
+
|
113
|
+
@current_test[:steps] << step
|
114
|
+
@test_steps << step
|
115
|
+
end
|
116
|
+
|
117
|
+
def determine_test_status(steps)
|
118
|
+
return 'failed' if steps.any? { |step| step[:status] == 'failed' }
|
119
|
+
return 'passed' if steps.any? { |step| step[:status] == 'passed' }
|
120
|
+
'running'
|
121
|
+
end
|
122
|
+
|
123
|
+
def generate_run_id
|
124
|
+
"#{Time.now.to_i}_#{SecureRandom.hex(4)}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -4,7 +4,6 @@ module CapyDash
|
|
4
4
|
class TestDataCollector
|
5
5
|
class << self
|
6
6
|
def start_test_run
|
7
|
-
CapyDash::Logger.info("Starting test run data collection")
|
8
7
|
@test_run_started = true
|
9
8
|
@test_count = 0
|
10
9
|
@passed_count = 0
|
@@ -14,12 +13,6 @@ module CapyDash
|
|
14
13
|
def finish_test_run
|
15
14
|
return unless @test_run_started
|
16
15
|
|
17
|
-
CapyDash::Logger.info("Finishing test run data collection", {
|
18
|
-
total_tests: @test_count,
|
19
|
-
passed: @passed_count,
|
20
|
-
failed: @failed_count
|
21
|
-
})
|
22
|
-
|
23
16
|
@test_run_started = false
|
24
17
|
end
|
25
18
|
|
@@ -27,11 +20,6 @@ module CapyDash
|
|
27
20
|
return unless @test_run_started
|
28
21
|
|
29
22
|
@test_count += 1
|
30
|
-
CapyDash::Logger.info("Starting test", {
|
31
|
-
test_name: test_name,
|
32
|
-
test_class: test_class,
|
33
|
-
test_method: test_method
|
34
|
-
})
|
35
23
|
|
36
24
|
# Emit test start event
|
37
25
|
CapyDash::EventEmitter.broadcast(
|
@@ -51,12 +39,6 @@ module CapyDash
|
|
51
39
|
@failed_count += 1
|
52
40
|
end
|
53
41
|
|
54
|
-
CapyDash::Logger.info("Finishing test", {
|
55
|
-
test_name: test_name,
|
56
|
-
status: status,
|
57
|
-
error_message: error_message
|
58
|
-
})
|
59
|
-
|
60
42
|
# Emit test finish event
|
61
43
|
event_data = {
|
62
44
|
step_name: "test_finish",
|
data/lib/capydash/version.rb
CHANGED
data/lib/capydash.rb
CHANGED
data/lib/tasks/capydash.rake
CHANGED
@@ -1,11 +1,67 @@
|
|
1
1
|
namespace :capydash do
|
2
|
-
desc "Start
|
2
|
+
desc "Start local server to view static HTML report"
|
3
3
|
task server: :environment do
|
4
|
-
|
5
|
-
|
6
|
-
CapyDash::DashboardServer.instance(port: port).start
|
4
|
+
require 'webrick'
|
5
|
+
require 'capydash/report_generator'
|
7
6
|
|
8
|
-
|
9
|
-
|
7
|
+
report_dir = File.join(Dir.pwd, "capydash_report")
|
8
|
+
|
9
|
+
unless Dir.exist?(report_dir)
|
10
|
+
puts "No report directory found. Run 'bundle exec rake capydash:report' first."
|
11
|
+
exit 1
|
12
|
+
end
|
13
|
+
|
14
|
+
# Try different ports if 5173 is busy
|
15
|
+
port = 5173
|
16
|
+
begin
|
17
|
+
server = WEBrick::HTTPServer.new(
|
18
|
+
Port: port,
|
19
|
+
DocumentRoot: report_dir,
|
20
|
+
Logger: WEBrick::Log.new(nil, WEBrick::Log::ERROR),
|
21
|
+
AccessLog: [],
|
22
|
+
BindAddress: '127.0.0.1'
|
23
|
+
)
|
24
|
+
rescue Errno::EADDRINUSE
|
25
|
+
port = 8080
|
26
|
+
puts "Port 5173 is busy, trying port #{port}..."
|
27
|
+
server = WEBrick::HTTPServer.new(
|
28
|
+
Port: port,
|
29
|
+
DocumentRoot: report_dir,
|
30
|
+
Logger: WEBrick::Log.new(nil, WEBrick::Log::ERROR),
|
31
|
+
AccessLog: [],
|
32
|
+
BindAddress: '127.0.0.1'
|
33
|
+
)
|
34
|
+
end
|
35
|
+
|
36
|
+
puts "Starting static file server on http://localhost:#{port}"
|
37
|
+
puts "Report available at: http://localhost:#{port}/index.html"
|
38
|
+
puts "Press Ctrl+C to stop the server"
|
39
|
+
|
40
|
+
trap("INT") {
|
41
|
+
puts "\nShutting down server..."
|
42
|
+
server.shutdown
|
43
|
+
}
|
44
|
+
|
45
|
+
begin
|
46
|
+
server.start
|
47
|
+
rescue => e
|
48
|
+
puts "Error starting server: #{e.message}"
|
49
|
+
exit 1
|
50
|
+
end
|
10
51
|
end
|
52
|
+
|
53
|
+
desc "Generate static HTML test report"
|
54
|
+
task report: :environment do
|
55
|
+
require 'capydash/report_generator'
|
56
|
+
|
57
|
+
report_path = CapyDash::ReportGenerator.generate_report
|
58
|
+
|
59
|
+
if report_path
|
60
|
+
puts "Report generated: file://#{File.absolute_path(report_path)}"
|
61
|
+
else
|
62
|
+
puts "No test data found. Run some tests first to generate a report."
|
63
|
+
exit 1
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
11
67
|
end
|
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.
|
4
|
+
version: 0.1.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Damon Clark
|
@@ -114,6 +114,9 @@ files:
|
|
114
114
|
- lib/capydash/instrumentation.rb
|
115
115
|
- lib/capydash/logger.rb
|
116
116
|
- lib/capydash/persistence.rb
|
117
|
+
- lib/capydash/report_generator.rb
|
118
|
+
- lib/capydash/templates/report.html.erb
|
119
|
+
- lib/capydash/test_data_aggregator.rb
|
117
120
|
- lib/capydash/test_data_collector.rb
|
118
121
|
- lib/capydash/version.rb
|
119
122
|
- lib/tasks/capydash.rake
|