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 =
vitest_unit: real Vitest's own unit-suffix color (#b9e4b4); no ANSI-16 entry matches it.
{ red: "31", green: "32", yellow: "33", cyan: "36", gray: "90", vitest_unit: "38;2;185;228;180" }.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).
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 108 |
# File 'lib/rutidy/render.rb', line 37 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
152 153 154 155 156 157 158 159 160 161 162 163 |
# File 'lib/rutidy/render.rb', line 152 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
138 139 140 141 142 143 144 145 146 147 148 149 150 |
# File 'lib/rutidy/render.rb', line 138 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(:vitest_unit, 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.
171 172 173 174 175 176 177 178 179 180 181 182 |
# File 'lib/rutidy/render.rb', line 171 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
189 190 191 192 193 194 |
# File 'lib/rutidy/render.rb', line 189 def format_backtrace(example) exception = example[:exception] return [] unless exception ["#{exception[:class]}: #{exception[:message]}"] + Array(exception[:backtrace]) end |
.format_seconds(seconds) ⇒ Object
196 197 198 |
# File 'lib/rutidy/render.rb', line 196 def format_seconds(seconds) (seconds < 1) ? format("%.4f", seconds) : format("%.2f", seconds) end |
.format_vitest_duration(seconds) ⇒ Object
207 208 209 210 |
# File 'lib/rutidy/render.rb', line 207 def format_vitest_duration(seconds) number, unit = format_vitest_duration_parts(seconds) "#{number}#{unit}" end |
.format_vitest_duration_parts(seconds) ⇒ Object
200 201 202 203 204 205 |
# File 'lib/rutidy/render.rb', line 200 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
184 185 186 187 |
# File 'lib/rutidy/render.rb', line 184 def pending_suffix(example) reason = example[:pending_message] reason ? "(PENDING: #{reason})" : "(PENDING)" end |
.vitest_summary_line(label, colorize, failed:, passed:, pending:, total:) ⇒ Object
129 130 131 132 133 134 135 136 |
# File 'lib/rutidy/render.rb', line 129 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
110 111 112 113 114 115 116 117 118 119 |
# File 'lib/rutidy/render.rb', line 110 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
121 122 123 124 125 126 127 |
# File 'lib/rutidy/render.rb', line 121 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 |