rspec-tracer 1.2.2 → 2.0.0.pre.1

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 (144) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +197 -45
  3. data/README.md +439 -429
  4. data/bin/rspec-tracer +15 -0
  5. data/lib/rspec_tracer/cache/Rakefile +43 -0
  6. data/lib/rspec_tracer/cli/cache_clear.rb +98 -0
  7. data/lib/rspec_tracer/cli/cache_info.rb +103 -0
  8. data/lib/rspec_tracer/cli/doctor.rb +275 -0
  9. data/lib/rspec_tracer/cli/explain.rb +148 -0
  10. data/lib/rspec_tracer/cli/report_open.rb +82 -0
  11. data/lib/rspec_tracer/cli.rb +116 -0
  12. data/lib/rspec_tracer/configuration.rb +1100 -3
  13. data/lib/rspec_tracer/engine.rb +1076 -0
  14. data/lib/rspec_tracer/example.rb +21 -6
  15. data/lib/rspec_tracer/filter.rb +35 -0
  16. data/lib/rspec_tracer/line_stub.rb +61 -0
  17. data/lib/rspec_tracer/load_config.rb +2 -2
  18. data/lib/rspec_tracer/logger.rb +15 -0
  19. data/lib/rspec_tracer/rails/README.md +78 -0
  20. data/lib/rspec_tracer/rails/i18n_tracking.rb +137 -0
  21. data/lib/rspec_tracer/rails/notifications.rb +263 -0
  22. data/lib/rspec_tracer/rails/preset.rb +94 -0
  23. data/lib/rspec_tracer/rails/railtie.rb +22 -0
  24. data/lib/rspec_tracer/rails.rb +15 -0
  25. data/lib/rspec_tracer/remote_cache/README.md +140 -0
  26. data/lib/rspec_tracer/remote_cache/Rakefile +35 -11
  27. data/lib/rspec_tracer/remote_cache/archive.rb +137 -0
  28. data/lib/rspec_tracer/remote_cache/backend.rb +73 -0
  29. data/lib/rspec_tracer/remote_cache/git_ancestry.rb +241 -0
  30. data/lib/rspec_tracer/remote_cache/local_fs_backend.rb +439 -0
  31. data/lib/rspec_tracer/remote_cache/redis_backend.rb +554 -0
  32. data/lib/rspec_tracer/remote_cache/s3_backend.rb +712 -0
  33. data/lib/rspec_tracer/remote_cache/user_tasks.rb +397 -0
  34. data/lib/rspec_tracer/remote_cache/validator.rb +40 -62
  35. data/lib/rspec_tracer/remote_cache.rb +22 -0
  36. data/lib/rspec_tracer/reporters/README.md +103 -0
  37. data/lib/rspec_tracer/reporters/base.rb +87 -0
  38. data/lib/rspec_tracer/reporters/coverage_json_reporter.rb +338 -0
  39. data/lib/rspec_tracer/reporters/html/.gitignore +19 -0
  40. data/lib/rspec_tracer/reporters/html/.prettierignore +4 -0
  41. data/lib/rspec_tracer/reporters/html/.prettierrc.json +9 -0
  42. data/lib/rspec_tracer/reporters/html/README.md +80 -0
  43. data/lib/rspec_tracer/reporters/html/dist/assets/index.css +2 -0
  44. data/lib/rspec_tracer/reporters/html/dist/assets/index.js +1 -0
  45. data/lib/rspec_tracer/reporters/html/dist/index.html +24 -0
  46. data/lib/rspec_tracer/reporters/html/eslint.config.js +62 -0
  47. data/lib/rspec_tracer/reporters/html/package-lock.json +4941 -0
  48. data/lib/rspec_tracer/reporters/html/package.json +29 -0
  49. data/lib/rspec_tracer/reporters/html/src/app.jsx +130 -0
  50. data/lib/rspec_tracer/reporters/html/src/components/AllExamples.jsx +86 -0
  51. data/lib/rspec_tracer/reporters/html/src/components/DuplicateExamples.jsx +68 -0
  52. data/lib/rspec_tracer/reporters/html/src/components/ExamplesDependency.jsx +78 -0
  53. data/lib/rspec_tracer/reporters/html/src/components/FilesDependency.jsx +72 -0
  54. data/lib/rspec_tracer/reporters/html/src/components/FlakyExamples.jsx +42 -0
  55. data/lib/rspec_tracer/reporters/html/src/components/ReportTable.jsx +131 -0
  56. data/lib/rspec_tracer/reporters/html/src/components/SearchBar.jsx +19 -0
  57. data/lib/rspec_tracer/reporters/html/src/index.html +23 -0
  58. data/lib/rspec_tracer/reporters/html/src/main.jsx +37 -0
  59. data/lib/rspec_tracer/reporters/html/src/styles.css +434 -0
  60. data/lib/rspec_tracer/reporters/html/vite.config.js +42 -0
  61. data/lib/rspec_tracer/reporters/html_reporter.rb +266 -0
  62. data/lib/rspec_tracer/reporters/json_reporter.rb +88 -0
  63. data/lib/rspec_tracer/reporters/payload_builder.rb +235 -0
  64. data/lib/rspec_tracer/reporters/registry.rb +120 -0
  65. data/lib/rspec_tracer/reporters/terminal_reporter.rb +264 -0
  66. data/lib/rspec_tracer/rspec/README.md +73 -0
  67. data/lib/rspec_tracer/rspec/installation.rb +97 -0
  68. data/lib/rspec_tracer/rspec/metadata.rb +96 -0
  69. data/lib/rspec_tracer/rspec/parallel_tests.rb +459 -0
  70. data/lib/rspec_tracer/rspec/reporter_hook.rb +84 -0
  71. data/lib/rspec_tracer/rspec/runner_hook.rb +178 -0
  72. data/lib/rspec_tracer/source_file.rb +24 -7
  73. data/lib/rspec_tracer/storage/README.md +35 -0
  74. data/lib/rspec_tracer/storage/backend.rb +68 -0
  75. data/lib/rspec_tracer/storage/json_backend.rb +866 -0
  76. data/lib/rspec_tracer/storage/lazy_snapshot.rb +65 -0
  77. data/lib/rspec_tracer/storage/schema.rb +43 -0
  78. data/lib/rspec_tracer/storage/serializer/json.rb +41 -0
  79. data/lib/rspec_tracer/storage/serializer/msgpack.rb +90 -0
  80. data/lib/rspec_tracer/storage/snapshot.rb +127 -0
  81. data/lib/rspec_tracer/storage/sqlite_backend.rb +686 -0
  82. data/lib/rspec_tracer/time_formatter.rb +37 -18
  83. data/lib/rspec_tracer/tracker/README.md +36 -0
  84. data/lib/rspec_tracer/tracker/coverage_adapter.rb +174 -0
  85. data/lib/rspec_tracer/tracker/declared_globs.rb +100 -0
  86. data/lib/rspec_tracer/tracker/dependency_graph.rb +134 -0
  87. data/lib/rspec_tracer/tracker/env_matcher.rb +127 -0
  88. data/lib/rspec_tracer/tracker/env_snapshot.rb +77 -0
  89. data/lib/rspec_tracer/tracker/example_registry.rb +153 -0
  90. data/lib/rspec_tracer/tracker/file_digest.rb +61 -0
  91. data/lib/rspec_tracer/tracker/filter.rb +127 -0
  92. data/lib/rspec_tracer/tracker/input.rb +99 -0
  93. data/lib/rspec_tracer/tracker/io_hooks/file.rb +55 -0
  94. data/lib/rspec_tracer/tracker/io_hooks/io.rb +24 -0
  95. data/lib/rspec_tracer/tracker/io_hooks/json.rb +23 -0
  96. data/lib/rspec_tracer/tracker/io_hooks/kernel.rb +26 -0
  97. data/lib/rspec_tracer/tracker/io_hooks/yaml.rb +38 -0
  98. data/lib/rspec_tracer/tracker/io_hooks.rb +195 -0
  99. data/lib/rspec_tracer/tracker/loaded_files_tracker.rb +295 -0
  100. data/lib/rspec_tracer/tracker/new_file_detector.rb +62 -0
  101. data/lib/rspec_tracer/tracker/whole_suite_invalidators.rb +96 -0
  102. data/lib/rspec_tracer/version.rb +4 -1
  103. data/lib/rspec_tracer.rb +232 -381
  104. metadata +93 -43
  105. data/lib/rspec_tracer/cache.rb +0 -207
  106. data/lib/rspec_tracer/coverage_merger.rb +0 -42
  107. data/lib/rspec_tracer/coverage_reporter.rb +0 -187
  108. data/lib/rspec_tracer/coverage_writer.rb +0 -58
  109. data/lib/rspec_tracer/html_reporter/Rakefile +0 -18
  110. data/lib/rspec_tracer/html_reporter/assets/javascripts/application.js +0 -56
  111. data/lib/rspec_tracer/html_reporter/assets/javascripts/libraries/jquery.js +0 -10881
  112. data/lib/rspec_tracer/html_reporter/assets/javascripts/plugins/datatables.js +0 -15381
  113. data/lib/rspec_tracer/html_reporter/assets/stylesheets/application.css +0 -196
  114. data/lib/rspec_tracer/html_reporter/assets/stylesheets/plugins/datatables.css +0 -459
  115. data/lib/rspec_tracer/html_reporter/assets/stylesheets/plugins/jquery-ui.css +0 -436
  116. data/lib/rspec_tracer/html_reporter/assets/stylesheets/print.css +0 -92
  117. data/lib/rspec_tracer/html_reporter/assets/stylesheets/reset.css +0 -265
  118. data/lib/rspec_tracer/html_reporter/public/application.css +0 -5
  119. data/lib/rspec_tracer/html_reporter/public/application.js +0 -6
  120. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_asc.png +0 -0
  121. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_asc_disabled.png +0 -0
  122. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_both.png +0 -0
  123. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_desc.png +0 -0
  124. data/lib/rspec_tracer/html_reporter/public/datatables/images/sort_desc_disabled.png +0 -0
  125. data/lib/rspec_tracer/html_reporter/public/favicon.png +0 -0
  126. data/lib/rspec_tracer/html_reporter/public/loading.gif +0 -0
  127. data/lib/rspec_tracer/html_reporter/reporter.rb +0 -242
  128. data/lib/rspec_tracer/html_reporter/views/duplicate_examples.erb +0 -34
  129. data/lib/rspec_tracer/html_reporter/views/examples.erb +0 -58
  130. data/lib/rspec_tracer/html_reporter/views/examples_dependency.erb +0 -36
  131. data/lib/rspec_tracer/html_reporter/views/files_dependency.erb +0 -36
  132. data/lib/rspec_tracer/html_reporter/views/flaky_examples.erb +0 -38
  133. data/lib/rspec_tracer/html_reporter/views/layout.erb +0 -38
  134. data/lib/rspec_tracer/remote_cache/aws.rb +0 -176
  135. data/lib/rspec_tracer/remote_cache/cache.rb +0 -75
  136. data/lib/rspec_tracer/remote_cache/repo.rb +0 -210
  137. data/lib/rspec_tracer/report_generator.rb +0 -158
  138. data/lib/rspec_tracer/report_merger.rb +0 -68
  139. data/lib/rspec_tracer/report_writer.rb +0 -141
  140. data/lib/rspec_tracer/reporter.rb +0 -204
  141. data/lib/rspec_tracer/rspec_reporter.rb +0 -41
  142. data/lib/rspec_tracer/rspec_runner.rb +0 -56
  143. data/lib/rspec_tracer/ruby_coverage.rb +0 -9
  144. data/lib/rspec_tracer/runner.rb +0 -278
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RSpecTracer
4
+ # Internal CLI — see {RSpecTracer} for the user-facing surface.
5
+ # @api private
6
+ module CLI
7
+ # `rspec-tracer report:open` — open the HTML report in the default
8
+ # browser. Resolves `report_path/index.html` and dispatches via
9
+ # `open` (macOS) / `xdg-open` (Linux). Falls back to printing the
10
+ # path when no opener is available.
11
+ module ReportOpen
12
+ # @param args [Array<String>] sub-command args (`-h` / `--help`).
13
+ # @param stdout [IO]
14
+ # @param stderr [IO]
15
+ # @return [Integer] exit status (0 = opened or path printed,
16
+ # 1 = report missing).
17
+ def self.run(args, stdout: $stdout, stderr: $stderr)
18
+ return print_help(stdout) if args.include?('-h') || args.include?('--help')
19
+
20
+ require 'rspec_tracer/load_config'
21
+
22
+ report_path = RSpecTracer.report_path
23
+ index_path = File.join(report_path, 'index.html')
24
+
25
+ unless File.file?(index_path)
26
+ stderr.puts "report:open: no report at #{index_path}"
27
+ stderr.puts ' run rspec first to generate the HTML report'
28
+ return 1
29
+ end
30
+
31
+ opener = detect_opener
32
+ if opener.nil?
33
+ stdout.puts "report:open: report at #{index_path}"
34
+ stdout.puts ' no opener detected (open / xdg-open); open the path manually'
35
+ return 0
36
+ end
37
+
38
+ if system(opener, index_path, out: File::NULL, err: File::NULL)
39
+ stdout.puts "report:open: opened #{index_path} via #{opener}"
40
+ 0
41
+ else
42
+ stderr.puts "report:open: failed to launch #{opener} #{index_path}"
43
+ 1
44
+ end
45
+ rescue StandardError => e
46
+ stderr.puts "report:open: #{e.class}: #{e.message}"
47
+ 1
48
+ end
49
+
50
+ # Internal helper for the tracer pipeline.
51
+ # @api private
52
+ def self.print_help(stdout)
53
+ stdout.puts <<~HELP
54
+ Usage: rspec-tracer report:open
55
+
56
+ Open the HTML report (`report_path/index.html`) in the default
57
+ browser. Uses `open` on macOS and `xdg-open` on Linux. Prints
58
+ the path and exits 0 when no opener is available.
59
+ HELP
60
+ 0
61
+ end
62
+
63
+ # Returns the opener binary name on PATH, or nil. Checked in
64
+ # priority order: macOS `open`, then Linux `xdg-open`. Windows
65
+ # is unsupported per `COMPATIBILITY_MATRIX.md`'s explicit drop;
66
+ # users on Windows see the print-the-path fallback.
67
+ def self.detect_opener
68
+ %w[open xdg-open].find { |bin| which(bin) }
69
+ end
70
+
71
+ # Internal helper for the tracer pipeline.
72
+ # @api private
73
+ def self.which(binary)
74
+ found = ENV.fetch('PATH', '').split(File::PATH_SEPARATOR).any? do |dir|
75
+ path = File.join(dir, binary)
76
+ File.file?(path) && File.executable?(path)
77
+ end
78
+ found ? binary : nil
79
+ end
80
+ end
81
+ end
82
+ end
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'optparse'
4
+
5
+ # Require the top-level entry to pull in `docile` (used by
6
+ # `Configuration#configure`) plus the autoloads sub-commands rely on.
7
+ # Loading `rspec_tracer` does NOT call `RSpecTracer.start` - the engine
8
+ # stays inert until a user invokes it explicitly.
9
+ require 'rspec_tracer'
10
+
11
+ module RSpecTracer
12
+ # Command-line entry for the `rspec-tracer` binary. Five sub-commands
13
+ # per USER_FACING_SURFACE.md §10: `doctor`, `cache:info`, `cache:clear`,
14
+ # `report:open`, `explain <example>`.
15
+ #
16
+ # The CLI is opt-in — the canonical CI flow continues to go through
17
+ # `rake rspec_tracer:remote_cache:*` tasks per USER_FACING_SURFACE.md
18
+ # §5. Sub-commands operate against the local cache + report
19
+ # directories resolved from the project's `.rspec-tracer` config
20
+ # (loaded lazily on first sub-command dispatch, not at CLI load time —
21
+ # `doctor` deliberately runs without requiring a configured project).
22
+ #
23
+ # @api private
24
+ module CLI
25
+ # Internal constant.
26
+ # @api private
27
+ SUB_COMMANDS = {
28
+ 'doctor' => 'Doctor',
29
+ 'cache:info' => 'CacheInfo',
30
+ 'cache:clear' => 'CacheClear',
31
+ 'report:open' => 'ReportOpen',
32
+ 'explain' => 'Explain'
33
+ }.freeze
34
+
35
+ # CLI entry. Called by `bin/rspec-tracer` with `ARGV`. Wraps every
36
+ # sub-command in a top-level rescue so the binary always exits
37
+ # with a meaningful integer status (0 / 1) instead of a backtrace.
38
+ #
39
+ # @param argv [Array<String>] command-line arguments (excluding
40
+ # the program name)
41
+ # @param stdout [IO] stream for normal output (default `$stdout`)
42
+ # @param stderr [IO] stream for errors / diagnostics (default `$stderr`)
43
+ # @return [Integer] exit status (0 = success, 1 = failure)
44
+ def self.run(argv, stdout: $stdout, stderr: $stderr)
45
+ args = argv.dup
46
+ return print_top_level_help(stdout) if args.empty? || %w[-h --help help].include?(args.first)
47
+ return print_version(stdout) if %w[-v --version].include?(args.first)
48
+
49
+ dispatch(args, stdout: stdout, stderr: stderr)
50
+ rescue StandardError => e
51
+ stderr.puts "rspec-tracer: #{e.class}: #{e.message}"
52
+ 1
53
+ end
54
+
55
+ # Internal helper for the tracer pipeline.
56
+ # @api private
57
+ def self.dispatch(args, stdout:, stderr:)
58
+ sub = args.shift
59
+ klass_name = SUB_COMMANDS[sub]
60
+ return unknown_sub_command(sub, stderr) if klass_name.nil?
61
+
62
+ load_sub_command(klass_name)
63
+ RSpecTracer::CLI.const_get(klass_name).run(args, stdout: stdout, stderr: stderr)
64
+ end
65
+
66
+ # Internal helper for the tracer pipeline.
67
+ # @api private
68
+ def self.unknown_sub_command(sub, stderr)
69
+ stderr.puts "rspec-tracer: unknown sub-command #{sub.inspect}"
70
+ stderr.puts " available: #{SUB_COMMANDS.keys.join(', ')}"
71
+ 1
72
+ end
73
+
74
+ # Internal helper for the tracer pipeline.
75
+ # @api private
76
+ def self.print_top_level_help(stdout)
77
+ stdout.puts <<~HELP
78
+ Usage: rspec-tracer <sub-command> [options]
79
+
80
+ Sub-commands:
81
+ doctor Diagnose rspec-tracer config + environment.
82
+ cache:info Show cache size, last run timestamp, and example counts.
83
+ cache:clear Remove cache, coverage, and report directories.
84
+ report:open Open the HTML report in the default browser.
85
+ explain <id> Show why an example is scheduled to run or skip.
86
+
87
+ Options:
88
+ -h, --help Print this help message.
89
+ -v, --version Print rspec-tracer version.
90
+
91
+ Run `rspec-tracer <sub-command> --help` for sub-command options.
92
+ HELP
93
+ 0
94
+ end
95
+
96
+ # Internal helper for the tracer pipeline.
97
+ # @api private
98
+ def self.print_version(stdout)
99
+ stdout.puts "rspec-tracer #{RSpecTracer::VERSION}"
100
+ 0
101
+ end
102
+
103
+ # Internal helper for the tracer pipeline.
104
+ # @api private
105
+ def self.load_sub_command(klass_name)
106
+ filename = klass_name.gsub(/([A-Z])/) { |m| "_#{m.downcase}" }.sub(/^_/, '')
107
+ require_relative "cli/#{filename}"
108
+ rescue LoadError => e
109
+ # LoadError isn't a StandardError, so the outer `rescue StandardError`
110
+ # in `.run` wouldn't catch it. Re-raise as StandardError so the dispatch
111
+ # path stays uniform: any sub-command resolution failure prints
112
+ # `rspec-tracer: <class>: <message>` and exits 1.
113
+ raise StandardError, "could not load sub-command #{klass_name.inspect}: #{e.message}"
114
+ end
115
+ end
116
+ end