minitest-bender 0.0.3 → 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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