capydash 0.2.1 → 0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 6a07047edb9a1e629b1a4974968f03f6aa92a7700b407f756019fa33acd4b79e
4
- data.tar.gz: aa1a16bfd2567198a45ba505185f52748f5e0a86d6b7a1abea5e7cb697fa3794
3
+ metadata.gz: 8120c468fac1e4d0e67b3190f2fd3905bf047a1628f66232cedbc3085e0af2f5
4
+ data.tar.gz: ea62c0dd462b95ad7a2405f0f139b9c9f3d5f3bfa0c9315b93e70ee39df094e0
5
5
  SHA512:
6
- metadata.gz: bc80a3f1ba97a2973cd54f98e1d0b945c3bf0b9c7507449ae75595f5a5edbb2373461fe4aeabbc0c9934d637de90eadcc0e3058b521a6c0b55b55c4715043fd0
7
- data.tar.gz: 544c3c1b1f3b37c43940d1c76e0bb61b0d7f2bf02ae8daa7800ffc43b8b1bf358744e472ee3701b27d1a99ee932e7b15c7ad1d468f66a0c9695fc0a92de3d537
6
+ metadata.gz: 0b2cbd4509f3dc292b2dc3873ae74e03fa3315cf850cd9bc10dbddfe63926c4cd03f1003fa728b527a3f48906149375709d432b347b774366607114209ec58a8
7
+ data.tar.gz: ec5f1fb549172b312df50562c60c3e40a641518acd2f2e86e8288882a6c17820d4550cfe5824188ed50ea59567758a7af085010156ebec8b5806fb117e46ac74
@@ -5,39 +5,18 @@ require 'erb'
5
5
  module CapyDash
6
6
  module RSpec
7
7
  class << self
8
- def setup!
9
- return unless defined?(::RSpec)
10
- return if @configured
11
-
12
- @configured = true
13
- @results = []
14
- @started_at = nil
15
-
16
- ::RSpec.configure do |config|
17
- config.before(:suite) do
18
- CapyDash::RSpec.start_run
19
- end
20
-
21
- config.after(:each) do |example|
22
- CapyDash::RSpec.record_example(example)
23
- end
24
-
25
- config.after(:suite) do
26
- CapyDash::RSpec.generate_report
27
- end
28
- end
29
- end
30
-
8
+ # Public method: Called from RSpec before(:suite) hook
31
9
  def start_run
32
10
  @results = []
33
11
  @started_at = Time.now
34
12
  end
35
13
 
14
+ # Public method: Called from RSpec after(:each) hook
36
15
  def record_example(example)
37
16
  return unless @started_at
38
17
 
39
18
  execution_result = example.execution_result
40
- status = execution_result.status.to_s
19
+ status = normalize_status(execution_result.status)
41
20
 
42
21
  error_message = nil
43
22
  if execution_result.status == :failed && execution_result.exception
@@ -56,6 +35,7 @@ module CapyDash
56
35
  }
57
36
  end
58
37
 
38
+ # Public method: Called from RSpec after(:suite) hook
59
39
  def generate_report
60
40
  return unless @started_at
61
41
  return if @results.empty?
@@ -108,8 +88,59 @@ module CapyDash
108
88
  report_dir
109
89
  end
110
90
 
91
+ # Public method: Sets up RSpec hooks
92
+ def setup!
93
+ return unless rspec_available?
94
+ return if @configured
95
+
96
+ begin
97
+ @configured = true
98
+ @results = []
99
+ @started_at = nil
100
+
101
+ ::RSpec.configure do |config|
102
+ config.before(:suite) do
103
+ CapyDash::RSpec.start_run
104
+ end
105
+
106
+ config.after(:each) do |example|
107
+ CapyDash::RSpec.record_example(example)
108
+ end
109
+
110
+ config.after(:suite) do
111
+ CapyDash::RSpec.generate_report
112
+ end
113
+ end
114
+ rescue => e
115
+ # If RSpec isn't ready, silently fail - it will be set up later
116
+ @configured = false
117
+ end
118
+ end
119
+
111
120
  private
112
121
 
122
+ def rspec_available?
123
+ return false unless defined?(::RSpec)
124
+ return false unless ::RSpec.respond_to?(:configure)
125
+ true
126
+ rescue
127
+ false
128
+ end
129
+
130
+ def normalize_status(status)
131
+ # Normalize RSpec status symbols to strings
132
+ case status
133
+ when :passed, 'passed'
134
+ 'passed'
135
+ when :failed, 'failed'
136
+ 'failed'
137
+ when :pending, 'pending'
138
+ 'pending'
139
+ else
140
+ status.to_s
141
+ end
142
+ end
143
+
113
144
  def extract_class_name(file_path)
114
145
  return 'UnknownSpec' if file_path.nil? || file_path.empty?
115
146
 
@@ -184,6 +215,26 @@ module CapyDash
184
215
  .header .subtitle {
185
216
  color: #666;
186
217
  font-size: 0.9rem;
218
+ margin-bottom: 1rem;
219
+ }
220
+
221
+ .search-container {
222
+ margin-top: 1rem;
223
+ }
224
+
225
+ .search-input {
226
+ width: 100%;
227
+ padding: 0.75rem 1rem;
228
+ border: 2px solid #ddd;
229
+ border-radius: 6px;
230
+ font-size: 1rem;
231
+ transition: border-color 0.2s;
232
+ }
233
+
234
+ .search-input:focus {
235
+ outline: none;
236
+ border-color: #3498db;
237
+ box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
187
238
  }
188
239
 
189
240
  .summary {
@@ -241,6 +292,10 @@ module CapyDash
241
292
  border-bottom: none;
242
293
  }
243
294
 
295
+ .test-class.hidden {
296
+ display: none;
297
+ }
298
+
244
299
  .test-class h2 {
245
300
  background: #f8f9fa;
246
301
  padding: 1rem 1.5rem;
@@ -259,6 +314,10 @@ module CapyDash
259
314
  border-bottom: none;
260
315
  }
261
316
 
317
+ .test-method.hidden {
318
+ display: none;
319
+ }
320
+
262
321
  .test-method-header {
263
322
  display: flex;
264
323
  align-items: center;
@@ -274,6 +333,30 @@ module CapyDash
274
333
  flex: 1;
275
334
  }
276
335
 
336
+ .method-status {
337
+ padding: 0.25rem 0.75rem;
338
+ border-radius: 4px;
339
+ font-size: 0.75rem;
340
+ font-weight: 600;
341
+ text-transform: uppercase;
342
+ letter-spacing: 0.5px;
343
+ }
344
+
345
+ .method-status-passed {
346
+ background: #27ae60;
347
+ color: white;
348
+ }
349
+
350
+ .method-status-failed {
351
+ background: #e74c3c;
352
+ color: white;
353
+ }
354
+
355
+ .method-status-pending {
356
+ background: #f39c12;
357
+ color: white;
358
+ }
359
+
277
360
  .expand-toggle {
278
361
  background: none;
279
362
  border: none;
@@ -408,6 +491,59 @@ module CapyDash
408
491
  }
409
492
  }
410
493
  }
494
+
495
+ // Search functionality
496
+ document.addEventListener('DOMContentLoaded', function() {
497
+ const searchInput = document.getElementById('searchInput');
498
+ if (!searchInput) return;
499
+
500
+ searchInput.addEventListener('input', function(e) {
501
+ const query = e.target.value.toLowerCase().trim();
502
+ const testMethods = document.querySelectorAll('.test-method');
503
+ const testClasses = document.querySelectorAll('.test-class');
504
+
505
+ // Determine if query is a status filter
506
+ const isStatusFilter = query === 'pass' || query === 'fail' ||
507
+ query === 'passed' || query === 'failed' ||
508
+ query === 'pending';
509
+
510
+ testMethods.forEach(function(method) {
511
+ const name = method.getAttribute('data-name') || '';
512
+ const status = method.getAttribute('data-status') || '';
513
+
514
+ let shouldShow = false;
515
+
516
+ if (!query) {
517
+ // No query - show all
518
+ shouldShow = true;
519
+ } else if (isStatusFilter) {
520
+ // Status filter - check status
521
+ shouldShow = (query === 'pass' && status === 'passed') ||
522
+ (query === 'fail' && status === 'failed') ||
523
+ query === status;
524
+ } else {
525
+ // Name filter - check if name contains query
526
+ shouldShow = name.includes(query);
527
+ }
528
+
529
+ if (shouldShow) {
530
+ method.classList.remove('hidden');
531
+ } else {
532
+ method.classList.add('hidden');
533
+ }
534
+ });
535
+
536
+ // Hide test classes if all methods are hidden
537
+ testClasses.forEach(function(testClass) {
538
+ const visibleMethods = testClass.querySelectorAll('.test-method:not(.hidden)');
539
+ if (visibleMethods.length === 0) {
540
+ testClass.classList.add('hidden');
541
+ } else {
542
+ testClass.classList.remove('hidden');
543
+ }
544
+ });
545
+ });
546
+ });
411
547
  JS
412
548
  end
413
549
  end
@@ -11,6 +11,10 @@
11
11
  <div class="header">
12
12
  <h1>CapyDash Test Report</h1>
13
13
  <div class="subtitle">Generated on <%= created_at.strftime('%B %d, %Y at %I:%M %p') %></div>
14
+
15
+ <div class="search-container">
16
+ <input type="text" id="searchInput" placeholder="Search tests or type 'pass' or 'fail' to filter..." class="search-input" autocomplete="off">
17
+ </div>
14
18
  </div>
15
19
 
16
20
  <div class="summary">
@@ -34,12 +38,13 @@
34
38
  <h2><%= test_class[:class_name] %></h2>
35
39
 
36
40
  <% test_class[:methods].each do |method| %>
37
- <div class="test-method">
41
+ <div class="test-method" data-status="<%= method[:status] %>" data-name="<%= method[:name].downcase %>">
38
42
  <div class="test-method-header">
39
43
  <button class="expand-toggle" onclick="toggleTestMethod('<%= method[:safe_id] %>')">
40
44
  <span class="expand-icon">▶</span>
41
45
  </button>
42
46
  <h3><%= method[:name] %></h3>
47
+ <span class="method-status method-status-<%= method[:status] %>"><%= method[:status] %></span>
43
48
  </div>
44
49
  <div class="steps collapsed" id="steps-<%= method[:safe_id] %>">
45
50
  <% method[:steps].each do |step| %>
@@ -1,3 +1,3 @@
1
1
  module CapyDash
2
- VERSION = "0.2.1"
2
+ VERSION = "0.2.3"
3
3
  end
data/lib/capydash.rb CHANGED
@@ -1,7 +1,15 @@
1
1
  require "capydash/version"
2
2
 
3
- # Auto-setup RSpec integration if RSpec is present
4
- if defined?(RSpec)
3
+ # Auto-setup RSpec integration if RSpec is present and ready
4
+ if defined?(RSpec) && RSpec.respond_to?(:configure)
5
5
  require "capydash/rspec"
6
6
  CapyDash::RSpec.setup!
7
+ elsif defined?(Rails)
8
+ # In Rails, RSpec might load after the gem, so set up a hook
9
+ Rails.application.config.after_initialize do
10
+ if defined?(RSpec) && RSpec.respond_to?(:configure)
11
+ require "capydash/rspec" unless defined?(CapyDash::RSpec)
12
+ CapyDash::RSpec.setup!
13
+ end
14
+ end
7
15
  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.2.1
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Damon Clark