rspec-ai-formatter 0.1.0 → 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: 78649016754440db1ab8030edcf686c32a35871bb8d967d3b2423e6677c6d68c
4
- data.tar.gz: 3d82c9e6e41523f4e914d6a66815755cbe7e59d5313feadc938fcbe6ec785904
3
+ metadata.gz: cc6a7dcbd86749ed1474847222a8ec4ecad876c2f8b2bc2b7db03ede73484b0b
4
+ data.tar.gz: a801f5b55489c3e0b2a101325cf3f91dffb396b067bedfd7a4f68e1e02267800
5
5
  SHA512:
6
- metadata.gz: 1b0d8fc0ef7a083fa584db00e2f11ad5dd0bb2a5456a7addca0ab10aac6ef6bd514ca39b24f208b82d5dcfda027ab20b2b9617a8a94fbe57acd1bcc33f76874c
7
- data.tar.gz: e03b3ac07503f063d3b3c9aa8c9ed3591dfb1fc85471aaa75b9d5c89c0c482762122d6187efe893c2e8ca5964ba90f202697d26ee032e22e5332363026f62763
6
+ metadata.gz: 02b285fe5fcd39a142700949d96da31c0997f0b60ed37eecc6f115d71bfb3fdd39733f11aff03bae94bd038374790a1412a00bf9fdfe5e2529128dbefdb3acd1
7
+ data.tar.gz: d7b2a3a616155ba2330044f9a39901afb3007863cde7832a31e5797374caa1014e53dff90788d854abc1ece0d73cf3d4e5c018f3a50f1e490d5e7b9770974a26
data/README.md CHANGED
@@ -98,32 +98,6 @@ Includes: test names, timestamps, timing for every test.
98
98
  rspec --format progress --format RSpec::AiFormatter::Formatter --out rspec.jsonl
99
99
  ```
100
100
 
101
- ### GitHub Actions
102
-
103
- The formatter auto-detects `GITHUB_ACTIONS=true` and emits workflow commands for inline PR annotations:
104
-
105
- ```yaml
106
- - name: Run tests
107
- env:
108
- GITHUB_ACTIONS: true # Usually already set by GitHub
109
- run: bundle exec rspec --format RSpec::AiFormatter::Formatter
110
- ```
111
-
112
- **Annotations appear on:**
113
- - Failed tests → `::error` with file/line
114
- - Skipped/pending tests → `::warning`
115
-
116
- **Example output:**
117
- ```
118
- ::error file=spec/models/user_spec.rb,line=42::expected false, got true
119
- ::warning file=spec/api/client_spec.rb,line=15::Skipped: pending implementation
120
- ```
121
-
122
- To disable annotations but keep NDJSON:
123
- ```bash
124
- GITHUB_ACTIONS= bundle exec rspec --format RSpec::AiFormatter::Formatter
125
- ```
126
-
127
101
  ### Parallel Tests
128
102
 
129
103
  When using `parallel_tests`, each process writes its own output file. Merge them with `rspec-ai-merge`:
@@ -161,6 +135,46 @@ rspec-ai-merge --no-summary file1.jsonl file2.jsonl
161
135
  - Combined `done` event with totals from all files
162
136
  - Deduplication summaries (if enabled)
163
137
 
138
+ ### Capybara Screenshots
139
+
140
+ For system tests using Capybara, the formatter automatically captures screenshots on failure:
141
+
142
+ ```ruby
143
+ # In your spec_helper.rb or rails_helper.rb
144
+ require 'capybara/rspec'
145
+
146
+ RSpec.configure do |config|
147
+ config.before(:each, type: :system) do
148
+ driven_by :selenium_chrome_headless
149
+ end
150
+ end
151
+ ```
152
+
153
+ When a system test fails, the screenshot path is included in the error output:
154
+
155
+ ```json
156
+ {
157
+ "t": "test",
158
+ "id": "spec/system/login_spec.rb:15",
159
+ "n": "User logs in successfully",
160
+ "s": "fail",
161
+ "e": {
162
+ "type": "ExpectationNotMet",
163
+ "msg": "expected to find text 'Welcome'",
164
+ "loc": "spec/system/login_spec.rb:20",
165
+ "log": "tmp/rspec_logs/login_spec_15.log",
166
+ "screenshot": "tmp/rspec_logs/login_spec_15.png"
167
+ }
168
+ }
169
+ ```
170
+
171
+ The screenshot is automatically saved to the log directory alongside the failure log.
172
+
173
+ **Requirements:**
174
+ - Capybara gem must be available
175
+ - Test type must be `:feature` or `:system`
176
+ - Browser driver must support screenshots (Selenium, Playwright, etc.)
177
+
164
178
  ## Output Format
165
179
 
166
180
  ### NDJSON Stream
@@ -233,6 +247,9 @@ spec/models/user_spec.rb:42:in `block (3 levels) in <top (required)>'
233
247
  | `msg` | Truncated message (200 chars) |
234
248
  | `loc` | File:line of failure |
235
249
  | `log` | Path to full log file |
250
+ | `screenshot` | Path to Capybara screenshot (system tests only) |
251
+ | `sig` | MD5 signature of error (if dedup/signatures enabled) |
252
+ | `diff` | Expected/actual diff (for expectations) |
236
253
  | `sig` | MD5 signature of error (if dedup/signatures enabled) |
237
254
  | `diff` | Expected/actual diff (for expectations) |
238
255
 
@@ -274,13 +291,10 @@ Useful when a shared setup/teardown failure cascades through many tests.
274
291
  - ✅ **Diff generation**: Smart expected/actual comparison
275
292
  - ✅ **Truncation**: Prevent token explosion on large outputs
276
293
  - ✅ **ANSI-free**: Clean text, no escape codes
277
- - ✅ **GitHub Actions**: Auto-detects and emits `::error`/`::warning` annotations
294
+ - ✅ **Screenshot capture**: Auto-capture Capybara screenshots on system test failures
278
295
 
279
296
  ## TODO
280
297
 
281
- ### CI/CD Integration
282
- - [x] GitHub Actions workflow command integration — Auto-detect `GITHUB_ACTIONS` env and emit `::error` / `::warning` commands for inline PR annotations
283
-
284
298
  ### Error Intelligence
285
299
  - [x] Error pattern deduplication across test runs — Group failures by signature hash, show "and 4 more like this"
286
300
  - [ ] Quick-fix hints integration with `did_you_mean` — Suggest typo corrections, similar method names
@@ -294,7 +308,7 @@ Useful when a shared setup/teardown failure cascades through many tests.
294
308
  ### Compatibility & Tooling
295
309
  - [ ] JUnit XML compatibility mode — Dual output for systems requiring XML
296
310
  - [x] Parallel test log merging — Clean merge of outputs from `parallel_tests` runs
297
- - [ ] Screenshot capture for system tests — Auto-capture Capybara screenshot path on failure
311
+ - [x] Screenshot capture for system tests — Auto-capture Capybara screenshot path on failure
298
312
  - [ ] Custom output templates — User-defined NDJSON schema / field selection
299
313
 
300
314
  ## License
@@ -4,7 +4,7 @@ module RSpec
4
4
  module AiFormatter
5
5
  # Formats error details for output
6
6
  module ErrorFormatter
7
- def error_details(example, notification, log_path, sig = nil)
7
+ def error_details(example, notification, log_path, sig = nil, screenshot_path = nil)
8
8
  exception = notification.exception
9
9
  return {} unless exception
10
10
 
@@ -15,6 +15,7 @@ module RSpec
15
15
  }
16
16
 
17
17
  details[:log] = log_path if log_path && @log_dir != File::NULL
18
+ details[:screenshot] = screenshot_path if screenshot_path
18
19
  details[:sig] = sig if sig
19
20
 
20
21
  # Add diff for expectation failures
@@ -7,6 +7,7 @@ require_relative 'log_writer'
7
7
  require_relative 'error_formatter'
8
8
  require_relative 'location_helper'
9
9
  require_relative 'output_helper'
10
+ require_relative 'screenshot_capture'
10
11
 
11
12
  module RSpec
12
13
  module AiFormatter
@@ -17,6 +18,7 @@ module RSpec
17
18
  include ErrorFormatter
18
19
  include LocationHelper
19
20
  include OutputHelper
21
+ include ScreenshotCapture
20
22
 
21
23
  RSpec::Core::Formatters.register self,
22
24
  :start,
@@ -75,13 +77,17 @@ module RSpec
75
77
  exception = notification.exception
76
78
  log_path = write_failure_log(ex, notification)
77
79
 
80
+ # Capture Capybara screenshot if available
81
+ screenshot_path = capture_screenshot(ex)
82
+ screenshot_log_path = copy_screenshot_to_logs(ex, screenshot_path) if screenshot_path
83
+
78
84
  # Calculate signature for deduplication
79
85
  sig = error_signature(exception) if @deduplicate || ENV['RSPEC_AI_SIGNATURES'] == '1'
80
86
 
81
87
  if @deduplicate && sig && @error_signatures.key?(sig)
82
88
  handle_duplicate_error(ex, sig)
83
89
  else
84
- handle_first_error(ex, notification, log_path, sig)
90
+ handle_first_error(ex, notification, log_path, sig, screenshot_log_path)
85
91
  end
86
92
  end
87
93
 
@@ -130,7 +136,7 @@ module RSpec
130
136
  )
131
137
  end
132
138
 
133
- def handle_first_error(example, notification, log_path, sig)
139
+ def handle_first_error(example, notification, log_path, sig, screenshot_path = nil)
134
140
  # First occurrence of this error
135
141
  if @deduplicate && sig
136
142
  @error_signatures[sig] = {
@@ -148,7 +154,7 @@ module RSpec
148
154
  n: short_name(example),
149
155
  s: 'fail',
150
156
  time: execution_time_ms(example),
151
- e: error_details(example, notification, log_path, sig),
157
+ e: error_details(example, notification, log_path, sig, screenshot_path),
152
158
  ts: timestamp
153
159
  )
154
160
  end
@@ -0,0 +1,54 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpec
4
+ module AiFormatter
5
+ # Captures Capybara screenshots on test failure
6
+ module ScreenshotCapture
7
+ def capture_screenshot(example)
8
+ return nil unless capybara_available?
9
+ return nil unless example.metadata[:type] == :feature || example.metadata[:type] == :system
10
+
11
+ screenshot_path = take_screenshot
12
+ return nil unless screenshot_path && File.exist?(screenshot_path)
13
+
14
+ screenshot_path
15
+ rescue StandardError
16
+ nil
17
+ end
18
+
19
+ def copy_screenshot_to_logs(example, screenshot_path)
20
+ return nil unless screenshot_path && @log_dir != File::NULL
21
+
22
+ file = example.metadata[:file_path]
23
+ line = example.metadata[:line_number]
24
+ filename = "#{File.basename(file, '.rb')}_#{line}.png"
25
+ dest_path = File.join(@log_dir, filename)
26
+
27
+ FileUtils.cp(screenshot_path, dest_path)
28
+ dest_path
29
+ rescue StandardError
30
+ nil
31
+ end
32
+
33
+ private
34
+
35
+ def capybara_available?
36
+ defined?(Capybara) && Capybara.respond_to?(:current_session)
37
+ end
38
+
39
+ def take_screenshot
40
+ # Try different Capybara screenshot methods
41
+ if Capybara.current_session.respond_to?(:save_screenshot)
42
+ # Generate unique temp path
43
+ temp_path = File.join(Dir.tmpdir, "rspec_ai_screenshot_#{Time.now.to_i}_#{rand(1000)}.png")
44
+ Capybara.current_session.save_screenshot(temp_path)
45
+ temp_path
46
+ elsif Capybara.respond_to?(:save_screenshot)
47
+ temp_path = File.join(Dir.tmpdir, "rspec_ai_screenshot_#{Time.now.to_i}_#{rand(1000)}.png")
48
+ Capybara.save_screenshot(temp_path)
49
+ temp_path
50
+ end
51
+ end
52
+ end
53
+ end
54
+ end
@@ -4,7 +4,7 @@ require_relative 'ai_formatter/formatter'
4
4
 
5
5
  module RSpec
6
6
  module AiFormatter
7
- VERSION = '0.1.0'
7
+ VERSION = '0.2.0'
8
8
 
9
9
  class Error < StandardError; end
10
10
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rspec-ai-formatter
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - SK
@@ -46,6 +46,7 @@ files:
46
46
  - lib/rspec/ai_formatter/location_helper.rb
47
47
  - lib/rspec/ai_formatter/log_writer.rb
48
48
  - lib/rspec/ai_formatter/output_helper.rb
49
+ - lib/rspec/ai_formatter/screenshot_capture.rb
49
50
  - lib/rspec_ai_formatter.rb
50
51
  homepage: https://github.com/sciencejet/rspec-ai-formatter
51
52
  licenses: