tdd-guard-rspec 0.1.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.
Files changed (3) hide show
  1. checksums.yaml +7 -0
  2. data/lib/tdd_guard_rspec/formatter.rb +127 -0
  3. metadata +85 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 3cad067cd532fad7f91309f8026eaaab5337c88f139d5f97b3242d68fe1d801c
4
+ data.tar.gz: 7db1c42445d642dda3511b6897f1e6bb06cfa7f08b39605c5dd085410f5f4312
5
+ SHA512:
6
+ metadata.gz: 76febe048a73141057a088baf3527d29ebdcf2bbe6b57e0fe17d967d39f8e5a527cd7fdf9d3046e8b7881479d62600104fd4e96f36cf1e0843d4ee51c77fa40b
7
+ data.tar.gz: 4790dc1eed3f738bc17cd9210b971aa60152bce66099af796f9cf34cc8b3a17ef36491a8d0bcf958db86a565455bf7d51a990701d2bd03f11ac956271af4e2a8
@@ -0,0 +1,127 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "json"
4
+ require "fileutils"
5
+ require "rspec/core/formatters/base_formatter"
6
+
7
+ module TddGuardRspec
8
+ # RSpec formatter that captures test results for TDD Guard validation.
9
+ # Mirrors the pytest reporter's single-class architecture.
10
+ class Formatter < RSpec::Core::Formatters::BaseFormatter
11
+ RSpec::Core::Formatters.register self,
12
+ :example_passed,
13
+ :example_failed,
14
+ :example_pending,
15
+ :message,
16
+ :dump_summary,
17
+ :close
18
+
19
+ DEFAULT_DATA_DIR = ".claude/tdd-guard/data"
20
+
21
+ def initialize(output)
22
+ super(output)
23
+ @test_results = []
24
+ @load_errors = []
25
+ @errors_outside = 0
26
+ @storage_dir = determine_storage_dir
27
+ end
28
+
29
+ def example_passed(notification)
30
+ record_example(notification.example, "passed")
31
+ end
32
+
33
+ def example_failed(notification)
34
+ example = notification.example
35
+ errors = [{ "message" => notification.exception.message }]
36
+ record_example(example, "failed", errors)
37
+ end
38
+
39
+ def example_pending(notification)
40
+ record_example(notification.example, "skipped")
41
+ end
42
+
43
+ def message(notification)
44
+ msg = notification.message
45
+ @load_errors << msg if msg.include?("An error occurred while loading")
46
+ end
47
+
48
+ def dump_summary(notification)
49
+ @errors_outside = notification.errors_outside_of_examples_count
50
+ end
51
+
52
+ def close(_notification)
53
+ add_load_error_results if @test_results.empty? && @errors_outside > 0
54
+
55
+ modules_map = {}
56
+ @test_results.each do |test|
57
+ module_path = test["fullName"].split("::").first
58
+ modules_map[module_path] ||= { "moduleId" => module_path, "tests" => [] }
59
+ modules_map[module_path]["tests"] << test
60
+ end
61
+
62
+ result = { "testModules" => modules_map.values }
63
+
64
+ FileUtils.mkdir_p(@storage_dir)
65
+ File.write(File.join(@storage_dir, "test.json"), JSON.pretty_generate(result))
66
+ end
67
+
68
+ private
69
+
70
+ def record_example(example, state, errors = nil)
71
+ file_path = example.file_path.sub(%r{^\./}, "")
72
+ test = {
73
+ "name" => example.description,
74
+ "fullName" => "#{file_path}::#{example.full_description}",
75
+ "state" => state
76
+ }
77
+ test["errors"] = errors if errors
78
+ @test_results << test
79
+ end
80
+
81
+ def add_load_error_results
82
+ @load_errors.each do |error_msg|
83
+ file_path = extract_file_path(error_msg)
84
+ error_name = extract_error_name(error_msg)
85
+ @test_results << {
86
+ "name" => error_name,
87
+ "fullName" => "#{file_path}::#{error_name}",
88
+ "state" => "failed",
89
+ "errors" => [{ "message" => error_msg.strip }]
90
+ }
91
+ end
92
+ end
93
+
94
+ def extract_file_path(error_msg)
95
+ match = error_msg.match(/An error occurred while loading (.+)\./)
96
+ return "unknown" unless match
97
+
98
+ match[1].sub(%r{^\./}, "").strip
99
+ end
100
+
101
+ def extract_error_name(error_msg)
102
+ match = error_msg.match(/^(\w+Error):\s*(.+?)$/m)
103
+ return "LoadError" unless match
104
+
105
+ "#{match[1]}: #{match[2].strip}"
106
+ end
107
+
108
+ def determine_storage_dir
109
+ project_root = ENV["TDD_GUARD_PROJECT_ROOT"]
110
+ return DEFAULT_DATA_DIR unless project_root && !project_root.empty?
111
+ return DEFAULT_DATA_DIR unless absolute_path?(project_root)
112
+ return DEFAULT_DATA_DIR unless cwd_within?(project_root)
113
+
114
+ File.join(project_root, DEFAULT_DATA_DIR)
115
+ end
116
+
117
+ def absolute_path?(path)
118
+ File.absolute_path?(path)
119
+ end
120
+
121
+ def cwd_within?(root)
122
+ expanded = File.expand_path(root)
123
+ cwd = Dir.pwd
124
+ cwd == expanded || cwd.start_with?("#{expanded}/")
125
+ end
126
+ end
127
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tdd-guard-rspec
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Hiro-Chiba
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2026-04-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rspec-core
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '3.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '3.0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rspec
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '3.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '3.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: climate_control
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.0'
55
+ description: RSpec formatter that captures test results for TDD Guard validation.
56
+ email:
57
+ executables: []
58
+ extensions: []
59
+ extra_rdoc_files: []
60
+ files:
61
+ - lib/tdd_guard_rspec/formatter.rb
62
+ homepage: https://github.com/nizos/tdd-guard
63
+ licenses:
64
+ - MIT
65
+ metadata: {}
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 3.3.0
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubygems_version: 3.3.15
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: RSpec formatter for TDD Guard - enforces Test-Driven Development principles
85
+ test_files: []