minitest-bender 0.0.3 → 1.0.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 (38) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +15 -1
  3. data/Gemfile +1 -1
  4. data/README.md +37 -6
  5. data/lib/minitest-bender/colorizer.rb +61 -0
  6. data/lib/minitest-bender/configuration.rb +174 -0
  7. data/lib/minitest-bender/printers/plain.rb +29 -0
  8. data/lib/minitest-bender/printers/with_progress_bar.rb +115 -0
  9. data/lib/minitest-bender/recorders/grouped_icons.rb +36 -0
  10. data/lib/minitest-bender/recorders/icons.rb +26 -0
  11. data/lib/minitest-bender/recorders/none.rb +17 -0
  12. data/lib/minitest-bender/recorders/progress.rb +25 -0
  13. data/lib/minitest-bender/recorders/progress_groups.rb +48 -0
  14. data/lib/minitest-bender/recorders/progress_groups_and_issues.rb +55 -0
  15. data/lib/minitest-bender/recorders/progress_issues.rb +32 -0
  16. data/lib/minitest-bender/recorders/progress_verbose.rb +34 -0
  17. data/lib/minitest-bender/result_context.rb +39 -0
  18. data/lib/minitest-bender/result_factory.rb +5 -0
  19. data/lib/minitest-bender/results/base.rb +94 -38
  20. data/lib/minitest-bender/results/expectation.rb +10 -8
  21. data/lib/minitest-bender/results/test.rb +21 -8
  22. data/lib/minitest-bender/sections/activity.rb +95 -0
  23. data/lib/minitest-bender/sections/issues.rb +22 -0
  24. data/lib/minitest-bender/sections/sorted_overview.rb +115 -0
  25. data/lib/minitest-bender/sections/suite_status.rb +72 -0
  26. data/lib/minitest-bender/sections/time_ranking.rb +49 -0
  27. data/lib/minitest-bender/states/base.rb +76 -19
  28. data/lib/minitest-bender/states/failing.rb +11 -8
  29. data/lib/minitest-bender/states/passing.rb +19 -8
  30. data/lib/minitest-bender/states/raising.rb +42 -20
  31. data/lib/minitest-bender/states/skipped.rb +12 -9
  32. data/lib/minitest-bender/utils.rb +26 -0
  33. data/lib/minitest-bender/version.rb +1 -1
  34. data/lib/minitest/bender.rb +166 -77
  35. data/lib/minitest/bender_plugin.rb +49 -3
  36. data/lib/minitest_bender.rb +23 -5
  37. data/minitest-bender.gemspec +6 -6
  38. metadata +39 -22
@@ -0,0 +1,36 @@
1
+ module MinitestBender
2
+ module Recorders
3
+ class GroupedIcons
4
+ def initialize(io)
5
+ @printer = Printers::Plain.new(io)
6
+ end
7
+
8
+ def print_context(result_context)
9
+ printer.print_line
10
+
11
+ context_path = result_context.path
12
+ context_separator = result_context.separator
13
+ prefix = result_context.prefix
14
+
15
+ path = context_path[0...-1].join(context_separator)
16
+ path << context_separator unless path.empty?
17
+ klass = context_path.last
18
+
19
+ printer.print("#{prefix}#{path}#{Colorizer.colorize(klass, :normal, :bold)} ")
20
+ end
21
+
22
+ def print_result(result)
23
+ printer.print(result.to_icon)
24
+ printer.advance
25
+ end
26
+
27
+ def print_context_with_results(_result_context, _results)
28
+ # do_nothing
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :printer
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,26 @@
1
+ module MinitestBender
2
+ module Recorders
3
+ class Icons
4
+ def initialize(io)
5
+ @printer = Printers::Plain.new(io)
6
+ end
7
+
8
+ def print_context(_result_context)
9
+ # do nothing
10
+ end
11
+
12
+ def print_result(result)
13
+ printer.print(result.to_icon)
14
+ printer.advance
15
+ end
16
+
17
+ def print_context_with_results(_result_context, _results)
18
+ # do_nothing
19
+ end
20
+
21
+ private
22
+
23
+ attr_reader :printer
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,17 @@
1
+ module MinitestBender
2
+ module Recorders
3
+ class None
4
+ def print_context(_result_context)
5
+ # do nothing
6
+ end
7
+
8
+ def print_result(_result)
9
+ # do nothing
10
+ end
11
+
12
+ def print_context_with_results(_result_context, _results)
13
+ # do_nothing
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,25 @@
1
+ module MinitestBender
2
+ module Recorders
3
+ class Progress
4
+ def initialize(io, total_tests_count)
5
+ @printer = Printers::WithProgressBar.new(io, total_tests_count)
6
+ end
7
+
8
+ def print_context(_result_context)
9
+ # do nothing
10
+ end
11
+
12
+ def print_result(result)
13
+ printer.advance
14
+ end
15
+
16
+ def print_context_with_results(_result_context, _results)
17
+ # do_nothing
18
+ end
19
+
20
+ private
21
+
22
+ attr_reader :printer
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,48 @@
1
+ module MinitestBender
2
+ module Recorders
3
+ class ProgressGroups
4
+ def initialize(io, total_tests_count)
5
+ @printer = Printers::WithProgressBar.new(io, total_tests_count)
6
+ @total_tests_count = total_tests_count
7
+ end
8
+
9
+ def print_context(_result_context)
10
+ # do nothing
11
+ end
12
+
13
+ def print_result(result)
14
+ printer.advance
15
+ end
16
+
17
+ def print_context_with_results(result_context, results)
18
+ context_path = result_context.path
19
+ context_separator = result_context.separator
20
+ prefix = result_context.prefix
21
+
22
+ path = context_path[0...-1].join(context_separator)
23
+ path << context_separator unless path.empty?
24
+ klass = context_path.last
25
+
26
+ printer.print_line("#{prefix}#{counters(result_context)} #{path}#{Colorizer.colorize(klass, :normal, :bold)}")
27
+ end
28
+
29
+ private
30
+
31
+ attr_reader :printer, :total_tests_count
32
+
33
+ def counters(result_context)
34
+ states.map do |state|
35
+ state.colored_icon_with_context_count(result_context, counters_padding_right)
36
+ end.join(' ')
37
+ end
38
+
39
+ def states
40
+ @states ||= MinitestBender.states.values
41
+ end
42
+
43
+ def counters_padding_right
44
+ @counters_padding_right ||= total_tests_count.to_s.size + 1
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,55 @@
1
+ module MinitestBender
2
+ module Recorders
3
+ class ProgressGroupsAndIssues
4
+ def initialize(io, total_tests_count)
5
+ @printer = Printers::WithProgressBar.new(io, total_tests_count)
6
+ @total_tests_count = total_tests_count
7
+ end
8
+
9
+ def print_context(_result_context)
10
+ # do nothing
11
+ end
12
+
13
+ def print_result(result)
14
+ printer.print_line(result_line(result)) unless result.passed?
15
+ lines = result.state.detail_lines_without_header(result)
16
+ printer.print_lines(lines)
17
+ printer.advance
18
+ end
19
+
20
+ def print_context_with_results(result_context, results)
21
+ context_path = result_context.path
22
+ context_separator = result_context.separator
23
+ prefix = result_context.prefix
24
+
25
+ path = context_path[0...-1].join(context_separator)
26
+ path << context_separator unless path.empty?
27
+ klass = context_path.last
28
+
29
+ printer.print_line("#{prefix}#{counters(result_context)} #{path}#{Colorizer.colorize(klass, :normal, :bold)}")
30
+ end
31
+
32
+ private
33
+
34
+ attr_reader :printer, :total_tests_count
35
+
36
+ def result_line(result)
37
+ " #{result.formatted_label_and_time}#{result.formatted_number} #{result.formatted_name_with_context}"
38
+ end
39
+
40
+ def counters(result_context)
41
+ states.map do |state|
42
+ state.colored_icon_with_context_count(result_context, counters_padding_right)
43
+ end.join(' ')
44
+ end
45
+
46
+ def states
47
+ @states ||= MinitestBender.states.values
48
+ end
49
+
50
+ def counters_padding_right
51
+ @counters_padding_right ||= total_tests_count.to_s.size + 1
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,32 @@
1
+ module MinitestBender
2
+ module Recorders
3
+ class ProgressIssues
4
+ def initialize(io, total_tests_count)
5
+ @printer = Printers::WithProgressBar.new(io, total_tests_count)
6
+ end
7
+
8
+ def print_context(_result_context)
9
+ # do nothing
10
+ end
11
+
12
+ def print_result(result)
13
+ printer.print_line(result_line(result)) unless result.passed?
14
+ lines = result.state.detail_lines_without_header(result)
15
+ printer.print_lines(lines)
16
+ printer.advance
17
+ end
18
+
19
+ def print_context_with_results(_result_context, _results)
20
+ # do_nothing
21
+ end
22
+
23
+ private
24
+
25
+ attr_reader :printer
26
+
27
+ def result_line(result)
28
+ " #{result.formatted_label_and_time}#{result.formatted_number} #{result.formatted_name_with_context}"
29
+ end
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,34 @@
1
+ module MinitestBender
2
+ module Recorders
3
+ class ProgressVerbose
4
+ def initialize(io, total_tests_count)
5
+ @printer = Printers::WithProgressBar.new(io, total_tests_count)
6
+ end
7
+
8
+ def print_context(result_context)
9
+ printer.print_line
10
+ printer.print_line(Colorizer.colorize(result_context.with_prefix, :normal, :bold))
11
+ end
12
+
13
+ def print_result(result)
14
+ printer.print_line(result_line(result))
15
+ lines = result.state.detail_lines_without_header(result)
16
+ padded_lines = lines.map { |line| " #{line}" }
17
+ printer.print_lines(padded_lines)
18
+ printer.advance
19
+ end
20
+
21
+ def print_context_with_results(_result_context, _results)
22
+ # do_nothing
23
+ end
24
+
25
+ private
26
+
27
+ attr_reader :printer
28
+
29
+ def result_line(result)
30
+ " #{result.formatted_label_and_time}#{result.formatted_number} #{result.name}"
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,39 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ module MinitestBender
5
+ class ResultContext < String
6
+ PREFIX = '• '
7
+ SEPARATOR = ' ▸ '
8
+ CLASS_SEPARATOR = '::'
9
+
10
+ def initialize(class_name)
11
+ @class_name = class_name
12
+ super(path.join(separator))
13
+ end
14
+
15
+ def path
16
+ class_name.split(class_separator)
17
+ end
18
+
19
+ def with_prefix
20
+ prefix + self
21
+ end
22
+
23
+ def separator
24
+ SEPARATOR
25
+ end
26
+
27
+ def prefix
28
+ PREFIX
29
+ end
30
+
31
+ private
32
+
33
+ attr_reader :class_name
34
+
35
+ def class_separator
36
+ CLASS_SEPARATOR
37
+ end
38
+ end
39
+ end
@@ -26,6 +26,11 @@ module MinitestBender
26
26
  end
27
27
 
28
28
  def parsed_name(minitest_result)
29
+ if minitest_result.name.is_a?(Class)
30
+ # something went wrong inside minitest (infinite loop?)
31
+ raise minitest_result.failures[0].error
32
+ end
33
+
29
34
  minitest_result.name.match(RESULT_NAME_REGEXP)
30
35
  end
31
36
  end
@@ -1,77 +1,133 @@
1
+ # coding: utf-8
2
+ # frozen_string_literal: true
3
+
4
+ require 'forwardable'
5
+
1
6
  module MinitestBender
2
7
  module Results
3
8
  class Base
4
9
  extend Forwardable
5
10
  def_delegators :@minitest_result, :passed?, :skipped?, :assertions, :failures, :time
11
+ attr_reader :state, :execution_order
12
+
13
+ NAME_PREFIX = '♦'
6
14
 
7
15
  def initialize(minitest_result)
8
16
  @minitest_result = minitest_result
9
17
  @state = MinitestBender.states.fetch(minitest_result.result_code)
18
+ @execution_order = state.add_result(self).size
10
19
  end
11
20
 
12
21
  def context
13
- @context ||=
14
- if minitest_result.respond_to?(:klass) # minitest >= 5.11
15
- minitest_result.klass
16
- else
17
- minitest_result.class.name
18
- end.gsub('::', ' > ')
22
+ @context ||= ResultContext.new(adjusted_class_name)
23
+ end
24
+
25
+ def to_icon
26
+ state.colored_icon
19
27
  end
20
28
 
21
- def header
22
- Colorin.white("• #{context}").bold
29
+ def name_sort_key
30
+ name
23
31
  end
24
32
 
25
- def details_header(number)
26
- " #{number}#{Colorin.white(context)} > #{name}"
33
+ def formatted_name_with_context
34
+ "#{Colorizer.colorize(context, :normal)} #{name_prefix} #{Colorizer.colorize(name, :normal, :bold)}"
27
35
  end
28
36
 
29
37
  def rerun_line(padding)
30
38
  unformatted = "Rerun: #{rerun_command}"
31
- "#{padding}#{Colorin.blue_a700(unformatted)}"
39
+ "#{padding}#{Colorizer.colorize(unformatted, :tests)}"
40
+ end
41
+
42
+ def formatted_label
43
+ state.formatted_label
44
+ end
45
+
46
+ def time_with_unit_and_padding_right
47
+ time_in_s = time
48
+ case time_in_s
49
+ when 0...1
50
+ sprintf('%.0fms ', time_in_s * 1000)
51
+ when 1...10
52
+ sprintf('%.2fs ', time_in_s)
53
+ when 10...100
54
+ sprintf('%.1fs ', time_in_s)
55
+ when 100...10000
56
+ sprintf('%.0fs ', time_in_s)
57
+ else
58
+ sprintf('%.0fs', time_in_s)
59
+ end
60
+ end
61
+
62
+ def formatted_time
63
+ Colorizer.colorize(time_with_unit_and_padding_right.rjust(6), :time)
64
+ end
65
+
66
+ def formatted_label_and_time
67
+ "#{formatted_label} #{formatted_time}"
32
68
  end
33
69
 
34
- def state?(some_state)
35
- state.class == some_state.class
70
+ def formatted_message
71
+ state.formatted_message(self)
72
+ end
73
+
74
+ def file_path
75
+ @file_path ||= source_location[0]
76
+ end
77
+
78
+ def source_line_number
79
+ @source_line_number ||= source_location[1]
36
80
  end
37
81
 
38
- def line_for_slowness_podium
39
- "#{formatted_time} #{Colorin.white(context)} > #{name}"
82
+ # credit where credit is due: minitest-line
83
+ def source_location
84
+ if minitest_at_least_5_11?
85
+ minitest_result.source_location
86
+ else
87
+ minitest_result.method(minitest_result.name).source_location rescue ['unknown', -1]
88
+ end
40
89
  end
41
90
 
42
91
  private
43
92
 
44
- attr_reader :minitest_result, :state
93
+ attr_reader :minitest_result
45
94
 
46
- def formatted_label
47
- " #{state.formatted_label}"
95
+ def adjusted_class_name
96
+ class_name
48
97
  end
49
98
 
50
- def formatted_message
51
- " #{state.formatted_message(self)}"
99
+ def class_name
100
+ if minitest_at_least_5_11?
101
+ minitest_result.klass
102
+ else
103
+ minitest_result.class.name
104
+ end
52
105
  end
53
106
 
54
- def formatted_time
55
- time_in_s = time
56
- time_with_unit =
57
- case time_in_s
58
- when 0...1
59
- sprintf('%.0fms ', time_in_s * 1000)
60
- when 1...10
61
- sprintf('%.2fs ', time_in_s)
62
- when 10...100
63
- sprintf('%.1fs ', time_in_s)
64
- when 100...10000
65
- sprintf('%.0fs ', time_in_s)
66
- else
67
- sprintf('%.0fs', time_in_s)
68
- end
69
- Colorin.grey_700(time_with_unit.rjust(6))
107
+ def minitest_at_least_5_11?
108
+ @minitest_at_least_5_11 ||= Gem.loaded_specs['minitest'].version >= Gem::Version.new('5.11')
109
+ end
110
+
111
+ def class_separator
112
+ CLASS_SEPARATOR
113
+ end
114
+
115
+ def name_prefix
116
+ NAME_PREFIX
70
117
  end
71
118
 
72
119
  def rerun_command
73
- relative_location = state.test_location(self).split(':').first
74
- "rake TEST=#{relative_location} TESTOPTS=\"--name=#{name_for_rerun_command}\""
120
+ return unless (relative_location = state.test_location(self))
121
+
122
+ relative_location = relative_location.split(':').first
123
+
124
+ stem = Minitest::Bender.configuration.rerun_command_stem
125
+
126
+ if stem.include?('rake')
127
+ "#{stem} TEST=#{relative_location} TESTOPTS=\"--name=#{name_for_rerun_command}\""
128
+ else
129
+ "#{stem} #{relative_location} --name=#{name_for_rerun_command}"
130
+ end
75
131
  end
76
132
  end
77
133
  end