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 +4 -4
- data/.github/workflows/ruby.yml +54 -0
- data/.rubocop.yml +5 -1
- data/README.md +80 -5
- data/exe/mt +6 -0
- data/lib/minitest/utils/cli.rb +310 -0
- data/lib/minitest/utils/extension.rb +19 -6
- data/lib/minitest/utils/rails.rb +4 -4
- data/lib/minitest/utils/reporter.rb +94 -65
- data/lib/minitest/utils/version.rb +1 -1
- data/lib/minitest/utils.rb +9 -10
- data/lib/minitest/utils_plugin.rb +29 -4
- data/minitest-utils.gemspec +3 -3
- metadata +14 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 220b58299506fd3ec789a5dc73b982f295863e3cd67cf9c807c94e08dad82f3f
|
4
|
+
data.tar.gz: fba7b309f9ee1c7b8f6ddc13bbbadc828b683c22d84ac2ad4c0c1ae9ff0a63e4
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
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
|
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
|
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) {
|
106
|
+
let(:token) { "secret" }
|
69
107
|
|
70
|
-
test
|
71
|
-
assert_equal
|
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
|

|
data/exe/mt
ADDED
@@ -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
|
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
|
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}"
|
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
|
-
|
57
|
+
time: nil,
|
58
|
+
slow_threshold:
|
52
59
|
}
|
53
60
|
|
54
61
|
testable = proc do
|
55
|
-
|
56
|
-
|
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
|
data/lib/minitest/utils/rails.rb
CHANGED
@@ -23,12 +23,12 @@ module ActiveSupport
|
|
23
23
|
|
24
24
|
require "minitest/utils/rails/capybara" if defined?(Capybara)
|
25
25
|
|
26
|
-
def t(
|
27
|
-
I18n.t(
|
26
|
+
def t(*, **)
|
27
|
+
I18n.t(*, **)
|
28
28
|
end
|
29
29
|
|
30
|
-
def l(
|
31
|
-
I18n.l(
|
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 ||= [
|
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
|
-
|
55
|
-
|
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
|
-
|
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
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
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["
|
295
|
+
if ENV["MT_TEST_COMMAND"]
|
296
|
+
cmd = ENV["MT_TEST_COMMAND"]
|
297
|
+
|
268
298
|
return format(
|
269
|
-
|
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
|
-
|
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
|
data/lib/minitest/utils.rb
CHANGED
@@ -3,12 +3,11 @@
|
|
3
3
|
module Minitest
|
4
4
|
module Utils
|
5
5
|
require "minitest"
|
6
|
-
require "benchmark"
|
7
6
|
require "pathname"
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
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
|
-
|
25
|
+
require_relative "utils/setup/webmock"
|
27
26
|
end
|
28
27
|
|
29
28
|
load_lib.call "database_cleaner" do
|
30
|
-
|
29
|
+
require_relative "utils/setup/database_cleaner"
|
31
30
|
end
|
32
31
|
|
33
32
|
load_lib.call "factory_girl" do
|
34
|
-
|
33
|
+
require_relative "utils/setup/factory_girl"
|
35
34
|
end
|
36
35
|
|
37
36
|
load_lib.call "factory_bot" do
|
38
|
-
|
37
|
+
require_relative "utils/setup/factory_bot"
|
39
38
|
end
|
40
39
|
|
41
|
-
|
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
|
-
|
4
|
-
|
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(
|
15
|
-
|
37
|
+
reporters << Minitest::Utils::TestNotifierReporter.new(
|
38
|
+
options[:io],
|
39
|
+
options
|
40
|
+
)
|
16
41
|
rescue LoadError
|
17
42
|
# noop
|
18
43
|
end
|
data/minitest-utils.gemspec
CHANGED
@@ -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.
|
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.
|
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-
|
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:
|
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:
|
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:
|
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.
|
163
|
+
version: 3.3.0
|
173
164
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
174
165
|
requirements:
|
175
166
|
- - ">="
|