minitest-utils 0.5.8 → 0.6.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 21397dacbcfc6383a5b9c1fe546808df9408702e8836253fc290d5ec970b1759
4
- data.tar.gz: ac528017206944beee38ce9cba466ad9978d9e16ec927b64c5a34bb7bbaf447a
3
+ metadata.gz: 220b58299506fd3ec789a5dc73b982f295863e3cd67cf9c807c94e08dad82f3f
4
+ data.tar.gz: fba7b309f9ee1c7b8f6ddc13bbbadc828b683c22d84ac2ad4c0c1ae9ff0a63e4
5
5
  SHA512:
6
- metadata.gz: 7bb1a97708504fc146d9d84a7b20d4e7c5cbefce960eabc57add36e72b219da18597566b932a936b528bf6ce0880a8a3dc54b67cd77a02323b9fd3e02fe9c374
7
- data.tar.gz: 5ac9e8d3e430f946126c877c1f029a5e0761651b4680b7ef096a2140fe55cbedc284f1e428d310160b8f561001dcc44e287e77537ba36e4666265c4e0f1eb05d
6
+ metadata.gz: 1c860200135f3ebbe01b8e6edc3ed77ea1ea2be5e03e73acee06396d916d6e09898eb98a6e1c0f6234a9b399acc192a56446655371182aef6b66199ee7154115
7
+ data.tar.gz: c6899dfce5f2aa135aa385a4fae57ea31387d29a673dcfd8daf857d54eb4e9cdf014d684f3820c29e0fef81a7898a4ab04677d572c6640e56515a6bc39f5c01c
@@ -0,0 +1,54 @@
1
+ ---
2
+ name: ruby-tests
3
+
4
+ on:
5
+ pull_request_target:
6
+ push:
7
+ branches:
8
+ - main
9
+ workflow_dispatch:
10
+ inputs: {}
11
+
12
+ jobs:
13
+ build:
14
+ name: Tests with Ruby ${{ matrix.ruby }} and ${{ matrix.gemfile }}
15
+ runs-on: "ubuntu-latest"
16
+ if: |
17
+ github.actor == 'dependabot[bot]' && github.event_name == 'pull_request_target' ||
18
+ github.actor != 'dependabot[bot]'
19
+ strategy:
20
+ fail-fast: false
21
+ matrix:
22
+ ruby: ["3.4", "3.3"]
23
+ gemfile:
24
+ - Gemfile
25
+
26
+ steps:
27
+ - uses: actions/checkout@v4
28
+
29
+ - uses: actions/cache@v4
30
+ with:
31
+ path: vendor/bundle
32
+ key: >
33
+ ${{ runner.os }}-${{ matrix.ruby }}-gems-${{
34
+ hashFiles(matrix.gemfile) }}-${{hashFiles('zee.gemspec')}}
35
+
36
+ - name: Set up Ruby
37
+ uses: ruby/setup-ruby@v1
38
+ with:
39
+ ruby-version: ${{ matrix.ruby }}
40
+
41
+ - name: Install gem dependencies
42
+ env:
43
+ BUNDLE_GEMFILE: ${{ matrix.gemfile }}
44
+ run: |
45
+ gem install bundler
46
+ bundle config set with default:development:test
47
+ bundle config path vendor/bundle
48
+ bundle update --jobs 4 --retry 3
49
+
50
+ - name: Run Tests
51
+ env:
52
+ BUNDLE_GEMFILE: ${{ matrix.gemfile }}
53
+ run: |
54
+ exe/mt
data/.rubocop.yml CHANGED
@@ -3,7 +3,11 @@ inherit_gem:
3
3
  rubocop-fnando: .rubocop.yml
4
4
 
5
5
  AllCops:
6
- TargetRubyVersion: 3.2
6
+ TargetRubyVersion: 3.3
7
+ NewCops: enable
8
+ Exclude:
9
+ - vendor/**/*
10
+ - gemfiles/**/*
7
11
 
8
12
  Style/GlobalVars:
9
13
  Exclude:
data/README.md CHANGED
@@ -32,7 +32,7 @@ methods like the following:
32
32
 
33
33
  ```ruby
34
34
  class SampleTest < Minitest::Test
35
- test 'useless test' do
35
+ test "useless test" do
36
36
  assert true
37
37
  end
38
38
  end
@@ -55,7 +55,45 @@ class SampleTest < Minitest::Test
55
55
  DB.disconnect
56
56
  end
57
57
 
58
- test 'useless test' do
58
+ test "useless test" do
59
+ assert true
60
+ end
61
+ end
62
+ ```
63
+
64
+ If you want to skip slow tests, you can use the `slow_test` method, which only
65
+ runs the test when `MT_RUN_SLOW_TESTS` environment variable is set.
66
+
67
+ ```ruby
68
+ # Only run slow tests in CI. You can bypass it locally by using
69
+ # something like `MT_RUN_SLOW_TESTS=1 rake`.
70
+ ENV["MT_RUN_SLOW_TESTS"] ||= ENV["CI"]
71
+
72
+ class SampleTest < Minitest::Test
73
+ test "useless test" do
74
+ slow_test
75
+ sleep 1
76
+ assert true
77
+ end
78
+ end
79
+ ```
80
+
81
+ You can change the default threshold by setting `Minitest::Test.slow_threshold`.
82
+ The default value is `0.1` (100ms).
83
+
84
+ ```ruby
85
+ Minitest::Test.slow_threshold = 0.1
86
+ ```
87
+
88
+ This config can also be changed per class:
89
+
90
+ ```ruby
91
+ class SampleTest < Minitest::Test
92
+ self.slow_threshold = 0.1
93
+
94
+ test "useless test" do
95
+ slow_test
96
+ sleep 1
59
97
  assert true
60
98
  end
61
99
  end
@@ -65,14 +103,51 @@ Finally, you can also use `let`.
65
103
 
66
104
  ```ruby
67
105
  class SampleTest < Minitest::Test
68
- let(:token) { 'secret' }
106
+ let(:token) { "secret" }
69
107
 
70
- test 'set token' do
71
- assert_equal 'secret', token
108
+ test "set token" do
109
+ assert_equal "secret", token
72
110
  end
73
111
  end
74
112
  ```
75
113
 
114
+ ## Running tests
115
+
116
+ `minitest-utils` comes with a runner: `mt`.
117
+
118
+ You can run specific files by using `file:number`.
119
+
120
+ ```console
121
+ $ mt test/models/user_test.rb:42
122
+ ```
123
+
124
+ You can also run files by the test name (caveat: you need to underscore the
125
+ name):
126
+
127
+ ```console
128
+ $ mt test/models/user_test.rb --name /validations/
129
+ ```
130
+
131
+ You can also run specific directories:
132
+
133
+ ```console
134
+ $ mt test/models
135
+ ```
136
+
137
+ To exclude tests by name, use --exclude:
138
+
139
+ ```console
140
+ $ mt test/models --exclude /validations/
141
+ ```
142
+
143
+ It supports `.minitestignore`, which only matches file names partially. Comments
144
+ starting with `#` are ignored.
145
+
146
+ ```
147
+ # Ignore all tests in test/fixtures
148
+ test/fixtures
149
+ ```
150
+
76
151
  ## Screenshots
77
152
 
78
153
  ![](https://raw.githubusercontent.com/fnando/minitest-utils/main/screenshots/light-failing.png)
data/exe/mt ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require_relative "../lib/minitest/utils/cli"
5
+ Minitest::Utils::CLI.loaded_via_bundle_exec = ENV.key?("BUNDLER_VERSION")
6
+ Minitest::Utils::CLI.new(ARGV.dup).start
@@ -0,0 +1,310 @@
1
+ # frozen_string_literal: true
2
+
3
+ gem "minitest"
4
+ require "minitest"
5
+ require_relative "../utils"
6
+ require "optparse"
7
+ require "io/console"
8
+
9
+ module Minitest
10
+ module Utils
11
+ class CLI
12
+ MATCHER =
13
+ /^(\s+(?:(?<short>-[a-zA-Z]+), )?(?<long>[^ ]+) +)(?<description>.*?)$/ # rubocop:disable Lint/MixedRegexpCaptureTypes
14
+
15
+ class << self
16
+ attr_accessor :loaded_via_bundle_exec
17
+ end
18
+
19
+ def initialize(args)
20
+ @args = args
21
+ end
22
+
23
+ def indent(text)
24
+ text.gsub(/^/, " ")
25
+ end
26
+
27
+ def start
28
+ OptionParser.new do |parser|
29
+ parser.banner = ""
30
+
31
+ parser.on_tail("-h", "--help", "Show this message") do
32
+ matches = parser.to_a.map do |line|
33
+ line.match(MATCHER).named_captures.transform_keys(&:to_sym)
34
+ end
35
+ print_help(matches)
36
+ exit
37
+ end
38
+
39
+ parser.on("-n", "--name=NAME",
40
+ "Run tests that match this name") do |v|
41
+ options[:name] = v
42
+ end
43
+
44
+ parser.on("-s", "--seed=SEED", "Sets fixed seed.") do |v|
45
+ options[:seed] = v
46
+ end
47
+
48
+ parser.on("--slow", "Run slow tests.") do |v|
49
+ options[:slow] = v
50
+ end
51
+
52
+ parser.on("--hide-slow", "Hide list of slow tests.") do |v|
53
+ options[:hide_slow] = v
54
+ end
55
+
56
+ parser.on("--slow-threshold=THRESHOLD",
57
+ "Set the slow threshold (in seconds)") do |v|
58
+ options[:slow_threshold] = v.to_f
59
+ end
60
+
61
+ parser.on(
62
+ "-e",
63
+ "--exclude=PATTERN",
64
+ "Exclude /regexp/ or string from run."
65
+ ) do |v|
66
+ options[:exclude] = v
67
+ end
68
+ end.parse!(@args)
69
+
70
+ run
71
+ end
72
+
73
+ def test_dir
74
+ File.join(Dir.pwd, "test")
75
+ end
76
+
77
+ def spec_dir
78
+ File.join(Dir.pwd, "spec")
79
+ end
80
+
81
+ def lib_dir
82
+ File.join(Dir.pwd, "lib")
83
+ end
84
+
85
+ def test_dir?
86
+ File.directory?(test_dir)
87
+ end
88
+
89
+ def spec_dir?
90
+ File.directory?(spec_dir)
91
+ end
92
+
93
+ def lib_dir?
94
+ File.directory?(lib_dir)
95
+ end
96
+
97
+ def run
98
+ $LOAD_PATH << lib_dir if lib_dir?
99
+ $LOAD_PATH << test_dir if test_dir?
100
+ $LOAD_PATH << spec_dir if spec_dir?
101
+
102
+ puts "\nNo tests found." if files.empty?
103
+
104
+ files.each {|file| require(file) }
105
+
106
+ bundler = "bundle exec " if self.class.loaded_via_bundle_exec
107
+
108
+ ENV["MT_TEST_COMMAND"] =
109
+ "#{bundler}mt %{location}:%{line} # %{description}"
110
+
111
+ ARGV.clear
112
+ ARGV.push(*minitest_args)
113
+ Minitest.autorun
114
+ end
115
+
116
+ def minitest_args
117
+ args = []
118
+ args += ["--seed", options[:seed]]
119
+ args += ["--exclude", options[:exclude]] if options[:exclude]
120
+ args += ["--slow", options[:slow]] if options[:slow]
121
+ args += ["--name", "/#{only.join('|')}/"] unless only.empty?
122
+ args += ["--hide-slow"] if options[:hide_slow]
123
+
124
+ if options[:slow_threshold]
125
+ threshold = options[:slow_threshold].to_s
126
+ threshold = threshold.gsub(/\.0+$/, "").delete_suffix(".")
127
+ args += ["--slow-threshold", threshold]
128
+ end
129
+
130
+ args.map(&:to_s)
131
+ end
132
+
133
+ def files
134
+ @files ||= begin
135
+ files = @args
136
+ files += %w[test spec] if files.empty?
137
+ files
138
+ .flat_map { expand_entry(_1) }
139
+ .reject { ignored_file?(_1) }
140
+ end
141
+ end
142
+
143
+ def ignored_files
144
+ @ignored_files ||= if File.file?(".minitestignore")
145
+ File.read(".minitestignore")
146
+ .lines
147
+ .map(&:strip)
148
+ .reject { _1.start_with?("#") }
149
+ else
150
+ []
151
+ end
152
+ end
153
+
154
+ def ignored_file?(file)
155
+ ignored_files.any? { file.include?(_1) }
156
+ end
157
+
158
+ def only
159
+ @only ||= []
160
+ end
161
+
162
+ def expand_entry(entry)
163
+ entry = extract_entry(entry)
164
+
165
+ if File.directory?(entry)
166
+ Dir[
167
+ File.join(entry, "**", "*_test.rb"),
168
+ File.join(entry, "**", "*_spec.rb")
169
+ ]
170
+ else
171
+ Dir[entry]
172
+ end
173
+ end
174
+
175
+ def extract_entry(entry)
176
+ entry = File.expand_path(entry)
177
+ return entry unless entry.match?(/:\d+$/)
178
+
179
+ entry, line = entry.split(":")
180
+ line = line.to_i
181
+ return entry unless File.file?(entry)
182
+
183
+ content = File.read(entry)
184
+ text = content.lines[line - 1].chomp.strip
185
+
186
+ method_name = if text =~ /^\s*test\s+(['"])(.*?)\1\s+do\s*$/
187
+ Test.test_method_name(::Regexp.last_match(2))
188
+ elsif text =~ /^def\s+(test_.+)$/
189
+ ::Regexp.last_match(1)
190
+ end
191
+
192
+ if method_name
193
+ class_names =
194
+ content.scan(/^\s*class\s+([^<]+)/).flatten.map(&:strip)
195
+
196
+ class_name = class_names.find do |name|
197
+ name.end_with?("Test")
198
+ end
199
+
200
+ only << "#{class_name}##{method_name}" if class_name
201
+ end
202
+
203
+ entry
204
+ end
205
+
206
+ def options
207
+ @options ||= {
208
+ seed: (ENV["SEED"] || srand).to_i % 0xFFFF
209
+ }
210
+ end
211
+
212
+ BANNER = <<~TEXT
213
+ A better test runner for Minitest.
214
+
215
+ You can run specific files by using `file:number`.
216
+
217
+ $ mt test/models/user_test.rb:42
218
+
219
+ You can also run files by the test name (caveat: you need to underscore the name):
220
+
221
+ $ mt test/models/user_test.rb --name /validations/
222
+
223
+ You can also run specific directories:
224
+
225
+ $ mt test/models
226
+
227
+ To exclude tests by name, use --exclude:
228
+
229
+ $ mt test/models --exclude /validations/
230
+ TEXT
231
+
232
+ COLOR = {
233
+ red: 31,
234
+ green: 32,
235
+ yellow: 33,
236
+ blue: 34,
237
+ gray: 37
238
+ }.freeze
239
+
240
+ private def color(string, color = :default)
241
+ return string if string.empty?
242
+
243
+ if $stdout.tty?
244
+ color = COLOR.fetch(color, 0)
245
+ "\e[#{color}m#{string}\e[0m"
246
+ else
247
+ string
248
+ end
249
+ end
250
+
251
+ def print_help(matches)
252
+ io = StringIO.new
253
+ matches.sort_by! { _1["long"] }
254
+ short_size = matches.map { _1[:short].to_s.size }.max
255
+ long_size = matches.map { _1[:long].to_s.size }.max
256
+
257
+ io << indent(color("Usage:", :green))
258
+ io << indent(color("mt [OPTIONS] [FILES|DIR]...", :blue))
259
+ io << "\n\n"
260
+ io << indent("A better test runner for Minitest.")
261
+ io << "\n\n"
262
+ file_line = color("file:number", :yellow)
263
+ io << indent("You can run specific files by using #{file_line}.")
264
+ io << "\n\n"
265
+ io << indent(color("$ mt test/models/user_test.rb:42", :yellow))
266
+ io << "\n\n"
267
+ io << indent("You can run files by the test name.")
268
+ io << "\n"
269
+ io << indent("Caveat: you need to underscore the name.")
270
+ io << "\n\n"
271
+ io << indent(
272
+ color("$ mt test/models/user_test.rb --name /validations/", :yellow)
273
+ )
274
+ io << "\n\n"
275
+ io << indent("You can also run specific directories:")
276
+ io << "\n\n"
277
+ io << indent(color("To exclude tests by name, use --exclude:", :yellow))
278
+ io << "\n\n"
279
+ io << indent("To ignore files, you can use a `.minitestignore`.")
280
+ io << "\n"
281
+ io << indent("Each line can be a partial file/dir name.")
282
+ io << "\n"
283
+ io << indent("Lines startin with # are ignored.")
284
+ io << "\n\n"
285
+ io << indent(color("# This is a comment", :yellow))
286
+ io << "\n"
287
+ io << indent(color("test/fixtures", :yellow))
288
+ io << "\n\n"
289
+ io << indent(color("Options:", :green))
290
+ io << "\n"
291
+
292
+ matches.each do |match|
293
+ match => { short:, long:, description: }
294
+
295
+ io << " "
296
+ io << (" " * (short_size - short.to_s.size))
297
+ io << color(short, :blue) if short
298
+ io << " " unless short
299
+ io << ", " if short
300
+ io << color(long.to_s, :blue)
301
+ io << (" " * (long_size - long.to_s.size + 4))
302
+ io << description
303
+ io << "\n"
304
+ end
305
+
306
+ puts io.tap(&:rewind).read
307
+ end
308
+ end
309
+ end
310
+ end
@@ -5,12 +5,12 @@ module Minitest
5
5
  module Assertions
6
6
  def assert(test, message = nil)
7
7
  message ||= "expected: truthy value\ngot: #{mu_pp(test)}"
8
- super(test, message)
8
+ super
9
9
  end
10
10
 
11
11
  def refute(test, message = nil)
12
12
  message ||= "expected: falsy value\ngot: #{mu_pp(test)}"
13
- super(test, message)
13
+ super
14
14
  end
15
15
  end
16
16
  end
@@ -22,13 +22,19 @@ module Minitest
22
22
  @tests ||= {}
23
23
  end
24
24
 
25
+ def slow_test
26
+ return if ENV["MT_RUN_SLOW_TESTS"] || Minitest.options[:slow]
27
+
28
+ skip "slow test"
29
+ end
30
+
25
31
  def self.test_method_name(description)
26
32
  method_name = description.downcase
27
33
  .gsub(/[^a-z0-9]+/, "_")
28
34
  .gsub(/^_+/, "")
29
35
  .gsub(/_+$/, "")
30
36
  .squeeze("_")
31
- "test_#{method_name}".to_sym
37
+ :"test_#{method_name}"
32
38
  end
33
39
 
34
40
  def self.test(description, &block)
@@ -48,12 +54,19 @@ module Minitest
48
54
  description:,
49
55
  name: test_name,
50
56
  source_location:,
51
- benchmark: nil
57
+ time: nil,
58
+ slow_threshold:
52
59
  }
53
60
 
54
61
  testable = proc do
55
- benchmark = Benchmark.measure { instance_eval(&block) }
56
- Test.tests["#{klass}##{test_name}"][:benchmark] = benchmark
62
+ err = nil
63
+ t0 = Minitest.clock_time
64
+ instance_eval(&block)
65
+ rescue StandardError => error
66
+ err = error
67
+ ensure
68
+ Test.tests["#{klass}##{test_name}"][:time] = Minitest.clock_time - t0
69
+ raise err if err
57
70
  end
58
71
 
59
72
  raise "#{test_name} is already defined in #{self}" if defined
@@ -23,12 +23,12 @@ module ActiveSupport
23
23
 
24
24
  require "minitest/utils/rails/capybara" if defined?(Capybara)
25
25
 
26
- def t(*args, **kwargs)
27
- I18n.t(*args, **kwargs)
26
+ def t(*, **)
27
+ I18n.t(*, **)
28
28
  end
29
29
 
30
- def l(*args, **kwargs)
31
- I18n.l(*args, **kwargs)
30
+ def l(*, **)
31
+ I18n.l(*, **)
32
32
  end
33
33
  end
34
34
  end
@@ -1,10 +1,21 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Minitest
4
+ class Test
5
+ class << self
6
+ attr_accessor :slow_threshold
7
+ end
8
+
9
+ def self.inherited(child)
10
+ child.slow_threshold = slow_threshold
11
+ super
12
+ end
13
+ end
14
+
4
15
  module Utils
5
16
  class Reporter < Minitest::StatisticsReporter
6
17
  def self.filters
7
- @filters ||= [/'Benchmark.measure'/]
18
+ @filters ||= []
8
19
  end
9
20
 
10
21
  COLOR_FOR_RESULT_CODE = {
@@ -32,17 +43,9 @@ module Minitest
32
43
  print_result_code(result.result_code)
33
44
  end
34
45
 
35
- def start
36
- super
37
- io.puts "Run options: #{options[:args]}"
38
- io.puts
39
- io.puts "# Running:"
40
- io.puts
41
- end
42
-
43
46
  def report
44
47
  super
45
- io.sync = true
48
+ io.sync = true if io.respond_to?(:sync)
46
49
 
47
50
  failing_results = results.reject(&:skipped?)
48
51
  skipped_results = results.select(&:skipped?)
@@ -51,17 +54,8 @@ module Minitest
51
54
  color = :yellow if skipped_results.any?
52
55
  color = :red if failing_results.any?
53
56
 
54
- if failing_results.any? || skipped_results.any?
55
- failing_results.each.with_index(1) do |result, index|
56
- display_failing(result, index)
57
- end
58
-
59
- skipped_results
60
- .each
61
- .with_index(failing_results.size + 1) do |result, index|
62
- display_skipped(result, index)
63
- end
64
- end
57
+ print_failing_results(failing_results)
58
+ print_skipped_results(skipped_results, failing_results.size)
65
59
 
66
60
  io.print "\n\n"
67
61
  io.puts statistics
@@ -72,46 +66,78 @@ module Minitest
72
66
  failing_results.each {|result| display_replay_command(result) }
73
67
  io.puts "\n\n"
74
68
  else
75
- threshold = 0.1 # 100ms
76
- test_results =
77
- Test
78
- .tests
79
- .values
80
- .select { _1[:benchmark] }
81
- .sort_by { _1[:benchmark].total }
82
- .reverse
83
- .take(10).filter { _1[:benchmark].total > threshold }
84
-
85
- return unless test_results.any?
86
-
87
- io.puts "\nSlow Tests:\n"
88
-
89
- test_results.each_with_index do |info, index|
90
- location = info[:source_location].join(":")
91
- duration = humanize_duration(info[:benchmark].total * 1_000_000_000)
92
-
93
- prefix = "#{index + 1}) "
94
- padding = " " * prefix.size
95
-
96
- io.puts color("#{prefix}#{info[:description]} (#{duration})", :red)
97
- io.puts color("#{padding}#{location}", :gray)
98
- io.puts
99
- end
69
+ print_slow_results
100
70
  end
101
71
  end
102
72
 
103
- private def humanize_duration(duration_ns)
104
- if duration_ns < 1000
105
- format("%.2f ns", duration_ns)
106
- elsif duration_ns < 1_000_000
107
- format("%.2f μs", (duration_ns / 1000))
108
- elsif duration_ns < 1_000_000_000
109
- format("%.2f ms", (duration_ns / 1_000_000))
110
- else
111
- format("%.2f s", (duration_ns / 1_000_000_000))
73
+ def slow_threshold_for(test_case)
74
+ test_case[:slow_threshold] || Minitest.options[:slow_threshold] || 0.1
75
+ end
76
+
77
+ def slow_tests
78
+ Test
79
+ .tests
80
+ .values
81
+ .select { _1[:time] }
82
+ .filter { _1[:time] > slow_threshold_for(_1) }
83
+ .sort_by { _1[:time] }
84
+ .reverse
85
+ end
86
+
87
+ def print_failing_results(results, initial_index = 1)
88
+ results.each.with_index(initial_index) do |result, index|
89
+ display_failing(result, index)
90
+ end
91
+ end
92
+
93
+ def print_skipped_results(results, initial_index)
94
+ results
95
+ .each
96
+ .with_index(initial_index + 1) do |result, index|
97
+ display_skipped(result, index)
98
+ end
99
+ end
100
+
101
+ def print_slow_results
102
+ test_results = slow_tests.take(10)
103
+
104
+ return if Minitest.options[:hide_slow]
105
+ return unless test_results.any?
106
+
107
+ io.puts "\nSlow Tests:\n"
108
+
109
+ test_results.each_with_index do |info, index|
110
+ location = info[:source_location].join(":")
111
+ duration = format_duration(info[:time])
112
+
113
+ prefix = "#{index + 1}) "
114
+ padding = " " * prefix.size
115
+
116
+ io.puts color("#{prefix}#{info[:description]} (#{duration})", :red)
117
+ io.puts color("#{padding}#{location}", :blue)
118
+ io.puts
112
119
  end
113
120
  end
114
121
 
122
+ def format_duration(duration_in_seconds)
123
+ duration_ns = duration_in_seconds * 1_000_000_000
124
+
125
+ number, unit = if duration_ns < 1000
126
+ [duration_ns, "ns"]
127
+ elsif duration_ns < 1_000_000
128
+ [duration_ns / 1000, "μs"]
129
+ elsif duration_ns < 1_000_000_000
130
+ [duration_ns / 1_000_000, "ms"]
131
+ else
132
+ [duration_ns / 1_000_000_000, "s"]
133
+ end
134
+
135
+ number =
136
+ format("%.2f", number).gsub(/0+$/, "").delete_suffix(".")
137
+
138
+ "#{number}#{unit}"
139
+ end
140
+
115
141
  private def statistics
116
142
  format(
117
143
  "Finished in %.6fs, %.4f runs/s, %.4f assertions/s.",
@@ -166,7 +192,11 @@ module Minitest
166
192
  output << color(
167
193
  format("%4d) %s [SKIPPED]", index, test[:description]), :yellow
168
194
  )
195
+
196
+ message = "Reason: #{result.failure.message}"
197
+ output << "\n" << indent(color(message, :yellow))
169
198
  output << "\n" << indent(color(location, :yellow))
199
+
170
200
  io.print output.join
171
201
  end
172
202
 
@@ -220,12 +250,6 @@ module Minitest
220
250
  .select {|line| line.start_with?(Dir.pwd) }
221
251
  end
222
252
 
223
- private def result_name(name)
224
- name
225
- .gsub(/^test(_\d+)?_/, "")
226
- .tr("_", " ")
227
- end
228
-
229
253
  private def print_result_code(result_code)
230
254
  result_code = color(result_code, COLOR_FOR_RESULT_CODE[result_code])
231
255
  io.print result_code
@@ -261,12 +285,18 @@ module Minitest
261
285
  Rails.version >= "5.0.0"
262
286
  end
263
287
 
288
+ def bundler
289
+ "bundle exec " if ENV.key?("BUNDLE_BIN_PATH")
290
+ end
291
+
264
292
  private def build_test_command(test, result)
265
293
  location, line = test[:source_location]
266
294
 
267
- if ENV["MINITEST_TEST_COMMAND"]
295
+ if ENV["MT_TEST_COMMAND"]
296
+ cmd = ENV["MT_TEST_COMMAND"]
297
+
268
298
  return format(
269
- ENV["MINITEST_TEST_COMMAND"],
299
+ cmd,
270
300
  location: location,
271
301
  line: line,
272
302
  description: test[:description],
@@ -277,8 +307,7 @@ module Minitest
277
307
  if running_rails?
278
308
  %[bin/rails test #{location}:#{line}]
279
309
  else
280
- bundle = "bundle exec " if defined?(Bundler)
281
- %[#{bundle}rake TEST=#{location} TESTOPTS="--name=#{result.name}"]
310
+ %[#{bundler}rake TEST=#{location} TESTOPTS="--name=#{result.name}"]
282
311
  end
283
312
  end
284
313
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Minitest
4
4
  module Utils
5
- VERSION = "0.5.8"
5
+ VERSION = "0.6.0"
6
6
  end
7
7
  end
@@ -3,12 +3,11 @@
3
3
  module Minitest
4
4
  module Utils
5
5
  require "minitest"
6
- require "benchmark"
7
6
  require "pathname"
8
- require "minitest/utils/version"
9
- require "minitest/utils/reporter"
10
- require "minitest/utils/extension"
11
- require "minitest/utils/test_notifier_reporter"
7
+ require_relative "utils/version"
8
+ require_relative "utils/reporter"
9
+ require_relative "utils/extension"
10
+ require_relative "utils/test_notifier_reporter"
12
11
 
13
12
  load_lib = lambda do |path, &block|
14
13
  require path
@@ -23,21 +22,21 @@ module Minitest
23
22
  load_lib.call "capybara"
24
23
 
25
24
  load_lib.call "webmock" do
26
- require "minitest/utils/setup/webmock"
25
+ require_relative "utils/setup/webmock"
27
26
  end
28
27
 
29
28
  load_lib.call "database_cleaner" do
30
- require "minitest/utils/setup/database_cleaner"
29
+ require_relative "utils/setup/database_cleaner"
31
30
  end
32
31
 
33
32
  load_lib.call "factory_girl" do
34
- require "minitest/utils/setup/factory_girl"
33
+ require_relative "utils/setup/factory_girl"
35
34
  end
36
35
 
37
36
  load_lib.call "factory_bot" do
38
- require "minitest/utils/setup/factory_bot"
37
+ require_relative "utils/setup/factory_bot"
39
38
  end
40
39
 
41
- require "minitest/utils/railtie" if defined?(Rails)
40
+ require_relative "utils/railtie" if defined?(Rails)
42
41
  end
43
42
  end
@@ -1,9 +1,32 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require "minitest/utils/reporter"
4
- require "minitest/utils/test_notifier_reporter"
3
+ require_relative "utils/reporter"
4
+ require_relative "utils/test_notifier_reporter"
5
5
 
6
6
  module Minitest
7
+ class << self
8
+ attr_accessor :options
9
+ end
10
+
11
+ self.options = {}
12
+
13
+ def self.plugin_utils_options(opts, options)
14
+ Minitest.options = options
15
+
16
+ opts.on("--slow", "Run slow tests") do
17
+ options[:slow] = true
18
+ end
19
+
20
+ opts.on("--hide-slow", "Hide list of slow tests") do
21
+ options[:hide_slow] = true
22
+ end
23
+
24
+ opts.on("--slow-threshold=THRESHOLD",
25
+ "Set the slow threshold (in seconds)") do |v|
26
+ options[:slow_threshold] = v.to_f
27
+ end
28
+ end
29
+
7
30
  def self.plugin_utils_init(options)
8
31
  reporters = Minitest.reporter.reporters
9
32
  reporters.clear
@@ -11,8 +34,10 @@ module Minitest
11
34
 
12
35
  begin
13
36
  require "test_notifier"
14
- reporters << Minitest::Utils::TestNotifierReporter.new(options[:io],
15
- options)
37
+ reporters << Minitest::Utils::TestNotifierReporter.new(
38
+ options[:io],
39
+ options
40
+ )
16
41
  rescue LoadError
17
42
  # noop
18
43
  end
@@ -10,7 +10,7 @@ Gem::Specification.new do |spec|
10
10
  spec.summary = "Some utilities for your Minitest day-to-day usage."
11
11
  spec.description = spec.summary
12
12
  spec.homepage = "http://github.com/fnando/minitest-utils"
13
- spec.required_ruby_version = Gem::Requirement.new(">= 3.2.0")
13
+ spec.required_ruby_version = Gem::Requirement.new(">= 3.3.0")
14
14
 
15
15
  spec.files = `git ls-files -z`
16
16
  .split("\x0")
@@ -19,12 +19,12 @@ Gem::Specification.new do |spec|
19
19
  spec.executables = spec.files.grep(%r{^exe/}) {|f| File.basename(f) }
20
20
  spec.require_paths = ["lib"]
21
21
 
22
- spec.add_dependency "benchmark"
23
22
  spec.add_dependency "minitest"
24
23
  spec.add_development_dependency "bundler"
25
- spec.add_development_dependency "pry-meta"
26
24
  spec.add_development_dependency "rake"
27
25
  spec.add_development_dependency "rubocop"
28
26
  spec.add_development_dependency "rubocop-fnando"
27
+ spec.add_development_dependency "simplecov"
29
28
  spec.add_development_dependency "test_notifier"
29
+ spec.metadata["rubygems_mfa_required"] = "true"
30
30
  end
metadata CHANGED
@@ -1,28 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: minitest-utils
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.8
4
+ version: 0.6.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Nando Vieira
8
8
  bindir: exe
9
9
  cert_chain: []
10
- date: 2025-03-14 00:00:00.000000000 Z
10
+ date: 2025-03-15 00:00:00.000000000 Z
11
11
  dependencies:
12
- - !ruby/object:Gem::Dependency
13
- name: benchmark
14
- requirement: !ruby/object:Gem::Requirement
15
- requirements:
16
- - - ">="
17
- - !ruby/object:Gem::Version
18
- version: '0'
19
- type: :runtime
20
- prerelease: false
21
- version_requirements: !ruby/object:Gem::Requirement
22
- requirements:
23
- - - ">="
24
- - !ruby/object:Gem::Version
25
- version: '0'
26
12
  - !ruby/object:Gem::Dependency
27
13
  name: minitest
28
14
  requirement: !ruby/object:Gem::Requirement
@@ -52,7 +38,7 @@ dependencies:
52
38
  - !ruby/object:Gem::Version
53
39
  version: '0'
54
40
  - !ruby/object:Gem::Dependency
55
- name: pry-meta
41
+ name: rake
56
42
  requirement: !ruby/object:Gem::Requirement
57
43
  requirements:
58
44
  - - ">="
@@ -66,7 +52,7 @@ dependencies:
66
52
  - !ruby/object:Gem::Version
67
53
  version: '0'
68
54
  - !ruby/object:Gem::Dependency
69
- name: rake
55
+ name: rubocop
70
56
  requirement: !ruby/object:Gem::Requirement
71
57
  requirements:
72
58
  - - ">="
@@ -80,7 +66,7 @@ dependencies:
80
66
  - !ruby/object:Gem::Version
81
67
  version: '0'
82
68
  - !ruby/object:Gem::Dependency
83
- name: rubocop
69
+ name: rubocop-fnando
84
70
  requirement: !ruby/object:Gem::Requirement
85
71
  requirements:
86
72
  - - ">="
@@ -94,7 +80,7 @@ dependencies:
94
80
  - !ruby/object:Gem::Version
95
81
  version: '0'
96
82
  - !ruby/object:Gem::Dependency
97
- name: rubocop-fnando
83
+ name: simplecov
98
84
  requirement: !ruby/object:Gem::Requirement
99
85
  requirements:
100
86
  - - ">="
@@ -124,11 +110,13 @@ dependencies:
124
110
  description: Some utilities for your Minitest day-to-day usage.
125
111
  email:
126
112
  - fnando.vieira@gmail.com
127
- executables: []
113
+ executables:
114
+ - mt
128
115
  extensions: []
129
116
  extra_rdoc_files: []
130
117
  files:
131
118
  - ".github/FUNDING.yml"
119
+ - ".github/workflows/ruby.yml"
132
120
  - ".gitignore"
133
121
  - ".rubocop.yml"
134
122
  - Gemfile
@@ -138,9 +126,11 @@ files:
138
126
  - bin/console
139
127
  - bin/rake
140
128
  - bin/setup
129
+ - exe/mt
141
130
  - lib/minitest/utils.rb
142
131
  - lib/minitest/utils/capybara/chrome_headless.rb
143
132
  - lib/minitest/utils/capybara/screenshot_on_failures.rb
133
+ - lib/minitest/utils/cli.rb
144
134
  - lib/minitest/utils/extension.rb
145
135
  - lib/minitest/utils/rails.rb
146
136
  - lib/minitest/utils/rails/capybara.rb
@@ -161,7 +151,8 @@ files:
161
151
  - screenshots/light-success.png
162
152
  homepage: http://github.com/fnando/minitest-utils
163
153
  licenses: []
164
- metadata: {}
154
+ metadata:
155
+ rubygems_mfa_required: 'true'
165
156
  rdoc_options: []
166
157
  require_paths:
167
158
  - lib
@@ -169,7 +160,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
169
160
  requirements:
170
161
  - - ">="
171
162
  - !ruby/object:Gem::Version
172
- version: 3.2.0
163
+ version: 3.3.0
173
164
  required_rubygems_version: !ruby/object:Gem::Requirement
174
165
  requirements:
175
166
  - - ">="