Module: Rutidy::Render
- Defined in:
- lib/rutidy/render.rb
Overview
Walks the leaves Formatter (or a real rspec --format json report
extended with a hierarchy array) produced and renders them as a nested
RSpec -fd-style tree, the same dedupe-shared-prefix walk gorderly's
render.go and xctidy's Engine.swift use, just over each example's
real hierarchy array instead of a /-split or comma-disambiguated
string. Four styles (:classic/:fd/:fs/:fv) share one closing
footer, except :fv, which closes with Vitest's own Test Files/
Tests/Duration shape.
Unlike gorderly, there's no fake "package path" label above the tree --
RSpec has no equivalent concept, so the first top-level describe is
just the first real node. A blank line still goes before every
top-level group, including the first -- that's not a gorderly-style
workaround here, it's genuine RSpec -fd behavior (its own
DocumentationFormatter#example_group_started does `output.puts if
Defined Under Namespace
Classes: FailureEntry
Constant Summary collapse
- ANSI_PREFIX =
"\e["- COLORS =
{ red: "31", green: "32", bright_green: "92", yellow: "33", cyan: "36", gray: "90" }.freeze
Class Method Summary collapse
-
.call(examples, style:, out:, color: true) ⇒ Integer
The number of failed examples (0 means a clean run).
- .colorize_fail(style, name, example, n, colorize) ⇒ Object
- .colorize_pass(style, name, example, colorize) ⇒ Object
-
.colorize_pending(style, name, example, colorize) ⇒ Object
RSpec's own vocabulary is "pending" -- not "skipped" the way Go/Ginkgo call it -- so every style says PENDING, matching what
rspec's real-fd/--format documentationwould say for the same example. - .format_backtrace(example) ⇒ Object
- .format_seconds(seconds) ⇒ Object
- .format_vitest_duration(seconds) ⇒ Object
- .format_vitest_duration_parts(seconds) ⇒ Object
- .pending_suffix(example) ⇒ Object
- .vitest_summary_line(label, colorize, failed:, passed:, pending:, total:) ⇒ Object
- .write_shared_footer(out, colorize, total:, failed_count:, pending:, total_elapsed:) ⇒ Object
- .write_vitest_footer(out, colorize, total:, failed_count:, pending:, total_elapsed:, files_total:, files_failed:) ⇒ Object
Class Method Details
.call(examples, style:, out:, color: true) ⇒ Integer
Returns the number of failed examples (0 means a clean run).
36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 |
# File 'lib/rutidy/render.rb', line 36 def call(examples, style:, out:, color: true) colorize = ->(name, text) { color ? "#{ANSI_PREFIX}#{COLORS.fetch(name)}m#{text}#{ANSI_PREFIX}0m" : text } total = 0 pending = 0 failed_count = 0 total_elapsed = 0.0 failures = [] files_total = {} files_failed = {} prev_path = [] examples.each do |example| hierarchy = example.fetch(:hierarchy) path = hierarchy[0..-2] leaf_name = hierarchy.last shared = 0 shared += 1 while shared < prev_path.length && shared < path.length && prev_path[shared] == path[shared] out.puts if shared.zero? (shared...path.length).each { |i| out.puts("#{" " * i}#{path[i]}") } total += 1 total_elapsed += example[:run_time] indent = " " * path.length file = example[:file_path] files_total[file] = true if file case example[:status] when "failed" failed_count += 1 files_failed[file] = true if file n = failures.length + 1 failures << FailureEntry.new(n, hierarchy, format_backtrace(example)) out.puts("#{indent}#{colorize_fail(style, leaf_name, example, n, colorize)}") when "pending" pending += 1 out.puts("#{indent}#{colorize_pending(style, leaf_name, example, colorize)}") else out.puts("#{indent}#{colorize_pass(style, leaf_name, example, colorize)}") end prev_path = path end out.puts if failures.any? out.puts("Failures:") failures.each do |f| out.puts out.puts(" #{f.n}) #{f.full.join(" ")}") f.output.each { |line| out.puts(" #{line}") } end out.puts end if style == :fv ( out, colorize, total: total, failed_count: failed_count, pending: pending, total_elapsed: total_elapsed, files_total: files_total.size, files_failed: files_failed.size ) else ( out, colorize, total: total, failed_count: failed_count, pending: pending, total_elapsed: total_elapsed ) end failed_count end |
.colorize_fail(style, name, example, n, colorize) ⇒ Object
151 152 153 154 155 156 157 158 159 160 161 162 |
# File 'lib/rutidy/render.rb', line 151 def colorize_fail(style, name, example, n, colorize) case style when :classic "#{colorize.call(:red, "✖")} #{name} (FAILED - #{n}) (#{colorize.call(:red, format_seconds(example[:run_time]))} seconds)" when :fs colorize.call(:red, "✗ #{name} (FAILED - #{n})") when :fv colorize.call(:red, "× #{name} #{format_vitest_duration(example[:run_time])}") else # :fd colorize.call(:red, "#{name} (FAILED - #{n})") end end |
.colorize_pass(style, name, example, colorize) ⇒ Object
137 138 139 140 141 142 143 144 145 146 147 148 149 |
# File 'lib/rutidy/render.rb', line 137 def colorize_pass(style, name, example, colorize) case style when :classic "#{colorize.call(:green, "✔")} #{name} (#{colorize.call(:green, format_seconds(example[:run_time]))} seconds)" when :fs "#{colorize.call(:green, "✔")} #{colorize.call(:gray, name)}" when :fv number, unit = format_vitest_duration_parts(example[:run_time]) "#{colorize.call(:green, "✓")} #{name} #{colorize.call(:green, number)}#{colorize.call(:bright_green, unit)}" else # :fd colorize.call(:green, name) end end |
.colorize_pending(style, name, example, colorize) ⇒ Object
RSpec's own vocabulary is "pending" -- not "skipped" the way Go/Ginkgo
call it -- so every style says PENDING, matching what rspec's real
-fd/--format documentation would say for the same example. Include
the pending reason where RSpec itself would (-fd/-fs); :classic
and :fv stay terse (glyph/name/time only), matching how gorderly's
classic and Vitest's own tree both treat a skip.
170 171 172 173 174 175 176 177 178 179 180 181 |
# File 'lib/rutidy/render.rb', line 170 def colorize_pending(style, name, example, colorize) case style when :classic "#{colorize.call(:cyan, "⊘")} #{name} (#{colorize.call(:cyan, format_seconds(example[:run_time]))} seconds)" when :fs colorize.call(:cyan, "- #{name} #{pending_suffix(example)}") when :fv colorize.call(:gray, "↓ #{name}") else # :fd colorize.call(:yellow, "#{name} #{pending_suffix(example)}") end end |
.format_backtrace(example) ⇒ Object
188 189 190 191 192 193 |
# File 'lib/rutidy/render.rb', line 188 def format_backtrace(example) exception = example[:exception] return [] unless exception ["#{exception[:class]}: #{exception[:message]}"] + Array(exception[:backtrace]) end |
.format_seconds(seconds) ⇒ Object
195 196 197 |
# File 'lib/rutidy/render.rb', line 195 def format_seconds(seconds) (seconds < 1) ? format("%.4f", seconds) : format("%.2f", seconds) end |
.format_vitest_duration(seconds) ⇒ Object
206 207 208 209 |
# File 'lib/rutidy/render.rb', line 206 def format_vitest_duration(seconds) number, unit = format_vitest_duration_parts(seconds) "#{number}#{unit}" end |
.format_vitest_duration_parts(seconds) ⇒ Object
199 200 201 202 203 204 |
# File 'lib/rutidy/render.rb', line 199 def format_vitest_duration_parts(seconds) ms = seconds * 1000 return [format("%.2f", ms / 1000), "s"] if ms > 1000 [ms.round.to_s, "ms"] end |
.pending_suffix(example) ⇒ Object
183 184 185 186 |
# File 'lib/rutidy/render.rb', line 183 def pending_suffix(example) reason = example[:pending_message] reason ? "(PENDING: #{reason})" : "(PENDING)" end |
.vitest_summary_line(label, colorize, failed:, passed:, pending:, total:) ⇒ Object
128 129 130 131 132 133 134 135 |
# File 'lib/rutidy/render.rb', line 128 def vitest_summary_line(label, colorize, failed:, passed:, pending:, total:) parts = [] parts << colorize.call(:red, "#{failed} failed") if failed.positive? parts << colorize.call(:green, "#{passed} passed") if passed.positive? parts << colorize.call(:gray, "#{pending} pending") if pending.positive? parts << "0 passed" if parts.empty? format("%11s %s (%d)", label, parts.join(" | "), total) end |
.write_shared_footer(out, colorize, total:, failed_count:, pending:, total_elapsed:) ⇒ Object
109 110 111 112 113 114 115 116 117 118 |
# File 'lib/rutidy/render.rb', line 109 def (out, colorize, total:, failed_count:, pending:, total_elapsed:) verdict = failed_count.positive? ? "Test Failed" : "Test Succeeded" verdict_color = failed_count.positive? ? :red : :green out.puts(colorize.call(verdict_color, verdict)) out.puts(colorize.call( verdict_color, format("Tests Passed: %d failed, %d pending, %d total (%s seconds)", failed_count, pending, total, format_seconds(total_elapsed)) )) end |
.write_vitest_footer(out, colorize, total:, failed_count:, pending:, total_elapsed:, files_total:, files_failed:) ⇒ Object
120 121 122 123 124 125 126 |
# File 'lib/rutidy/render.rb', line 120 def (out, colorize, total:, failed_count:, pending:, total_elapsed:, files_total:, files_failed:) passed = total - failed_count - pending files_passed = files_total - files_failed out.puts(vitest_summary_line("Test Files", colorize, failed: files_failed, passed: files_passed, pending: 0, total: files_total)) out.puts(vitest_summary_line("Tests", colorize, failed: failed_count, passed: passed, pending: pending, total: total)) out.puts(format("%11s %s", "Duration", format_vitest_duration(total_elapsed))) end |