nicetest 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +3 -0
- data/LICENSE.txt +21 -0
- data/README.md +97 -0
- data/exe/nicetest +6 -0
- data/lib/minitest/nicetest_plugin.rb +34 -0
- data/lib/minitest/reporters_plugin.rb +49 -0
- data/lib/minitest/superdiff_plugin.rb +95 -0
- data/lib/nicetest/backtrace_filter.rb +85 -0
- data/lib/nicetest/cli.rb +212 -0
- data/lib/nicetest/logger.rb +47 -0
- data/lib/nicetest/version.rb +5 -0
- data/lib/nicetest.rb +19 -0
- data.tar.gz.sig +0 -0
- metadata +162 -0
- metadata.gz.sig +0 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: f8b1e58e35c866d01e21b5b8e397e05c3506002d3af25681a8178e6c26d4f2e3
|
4
|
+
data.tar.gz: 8f032d23008336df8010de48bb4a4cf872e4416d52a092ce76e231a7a09cb934
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 1cc1cd63f09d3d3c5a2abca7fcd6bf9b914eb61391efb146cb0c01c0c77139b32ee8a32d493f1cef49b69bcf8df9ebda39bda69607f84f94b39535f30ef7b0fd
|
7
|
+
data.tar.gz: a19a0494f63b94dd40cfa2a493d31eb0ee1eea53ab33b08fc05e775d715dfc9c3f8e0eda533e1923cbdbe984da52d6fd6c9325da67b13ed968955dbf86e5c217
|
checksums.yaml.gz.sig
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
The MIT License (MIT)
|
2
|
+
|
3
|
+
Copyright (c) 2024 Ian Ker-Seymer
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
7
|
+
in the Software without restriction, including without limitation the rights
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
10
|
+
furnished to do so, subject to the following conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be included in
|
13
|
+
all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,97 @@
|
|
1
|
+
# ☀️ nicetest
|
2
|
+
|
3
|
+
Enhance your minitest experience with nicetest—a gem offering a CLI, pretty
|
4
|
+
assertion diffs, pre-configured reporters, focus checks, and colorful backtrace
|
5
|
+
filters for seamless test running.
|
6
|
+
|
7
|
+
## Features
|
8
|
+
|
9
|
+
- A decent CLI for running tests, which is compatible with existing minitest plugin options
|
10
|
+
- Fancier diffs using [`super_diff`](https://github.com/mcmire/super_diff)
|
11
|
+
- Bundled [`minitest-reporters`](https://github.com/minitest-reporters/minitest-reporters), with defaults configured
|
12
|
+
- Bundled [`minitest-focus`](https://github.com/minitest/minitest-focus), with CI sanity check
|
13
|
+
- A pretty backtrace filter
|
14
|
+
|
15
|
+
## Usage
|
16
|
+
|
17
|
+
### Take it with you
|
18
|
+
|
19
|
+
`nicetest` runs well on most `minitest` suites without hassle, so you can just
|
20
|
+
use it wherever you happen to be:
|
21
|
+
|
22
|
+
```sh
|
23
|
+
# Gem install it
|
24
|
+
$ gem install nicetest
|
25
|
+
|
26
|
+
# Go into a repo
|
27
|
+
$ git clone https://github.com/Shopfy/liquid; cd liquid; bundle install
|
28
|
+
|
29
|
+
# Run the tests
|
30
|
+
$ nicetest --reporter doc test/integration/capture_test.rb
|
31
|
+
Started with run options --reporter doc --seed 12874
|
32
|
+
|
33
|
+
CaptureTest
|
34
|
+
test_increment_assign_score_by_bytes_not_characters PASS (0.00s)
|
35
|
+
test_captures_block_content_in_variable PASS (0.00s)
|
36
|
+
test_capture_to_variable_from_outer_scope_if_existing PASS (0.00s)
|
37
|
+
test_assigning_from_capture PASS (0.00s)
|
38
|
+
test_capture_with_hyphen_in_variable_name PASS (0.00s)
|
39
|
+
|
40
|
+
Finished in 0.00123s
|
41
|
+
5 tests, 5 assertions, 0 failures, 0 errors, 0 skips
|
42
|
+
```
|
43
|
+
|
44
|
+
### Add to project
|
45
|
+
|
46
|
+
|
47
|
+
Add to your `test_helper.rb`:
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
# test/test_helper.rb
|
51
|
+
|
52
|
+
require "nicetest"
|
53
|
+
|
54
|
+
# whatever else you do... can probably remove some stuff
|
55
|
+
```
|
56
|
+
|
57
|
+
Then, make it easy to run:
|
58
|
+
|
59
|
+
```sh
|
60
|
+
# Add to Gemfile
|
61
|
+
$ bundle add nicetest
|
62
|
+
|
63
|
+
# Make a bin/test executable
|
64
|
+
$ bundle binstub nicetest && mv bin/nicetest bin/test
|
65
|
+
```
|
66
|
+
|
67
|
+
You can run it now:
|
68
|
+
|
69
|
+
```sh
|
70
|
+
# Run tests
|
71
|
+
$ bin/test
|
72
|
+
|
73
|
+
# In CI
|
74
|
+
$ bin/test --reporter doc,junit
|
75
|
+
```
|
76
|
+
|
77
|
+
### What about Rakefile?
|
78
|
+
|
79
|
+
If you want to use Rake, just do this:
|
80
|
+
|
81
|
+
```ruby
|
82
|
+
# Rakefile
|
83
|
+
|
84
|
+
task :test do
|
85
|
+
sh("bin/test")
|
86
|
+
end
|
87
|
+
```
|
88
|
+
|
89
|
+
## Contributing
|
90
|
+
|
91
|
+
Bug reports and pull requests are welcome on GitHub at
|
92
|
+
https://github.com/ianks/nicetest.
|
93
|
+
|
94
|
+
## License
|
95
|
+
|
96
|
+
The gem is available as open source under the terms of the [MIT
|
97
|
+
License](https://opensource.org/licenses/MIT).
|
data/exe/nicetest
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
# opts.banner = "Usage: example.rb [options]"
|
4
|
+
require "nicetest/cli"
|
5
|
+
|
6
|
+
module Minitest
|
7
|
+
class << self
|
8
|
+
def plugin_nicetest_options(opts, options)
|
9
|
+
opts.banner = Nicetest::Cli::BANNER
|
10
|
+
ValidateMinitestFocus.apply!
|
11
|
+
end
|
12
|
+
|
13
|
+
def plugin_nicetest_init(_options)
|
14
|
+
Minitest.backtrace_filter = Nicetest::BacktraceFilter.new
|
15
|
+
end
|
16
|
+
|
17
|
+
module ValidateMinitestFocus
|
18
|
+
class << self
|
19
|
+
def apply!
|
20
|
+
if ENV["CI"]
|
21
|
+
Minitest::Test.singleton_class.prepend(ValidateMinitestFocus::NoFocus)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
module NoFocus
|
27
|
+
def focus(name = nil)
|
28
|
+
location = caller_locations(1, 1).first
|
29
|
+
Nicetest.logger.fatal!("cannot use `focus` in CI (#{location.path}:#{location.lineno})")
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,49 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Minitest
|
4
|
+
module ReportersPlugin
|
5
|
+
MAPPING = {
|
6
|
+
"none" => ->(_options) { nil },
|
7
|
+
"default" => ->(options) { Minitest::Reporters::DefaultReporter.new(io: options[:io]) },
|
8
|
+
"spec" => ->(options) { Minitest::Reporters::SpecReporter.new(io: options[:io]) },
|
9
|
+
"doc" => ->(options) { MAPPING["spec"].call(options) },
|
10
|
+
"junit" => ->(options) {
|
11
|
+
ENV["MINITEST_REPORTERS_REPORTS_DIR"] ||= "tmp/nicetest/junit/#{Time.now.to_i}"
|
12
|
+
Minitest::Reporters::JUnitReporter.new(io: options[:io])
|
13
|
+
},
|
14
|
+
"progress" => ->(options) { Minitest::Reporters::ProgressReporter.new(io: options[:io]) },
|
15
|
+
}
|
16
|
+
end
|
17
|
+
|
18
|
+
class << self
|
19
|
+
def plugin_reporters_options(opts, options)
|
20
|
+
options[:reporters] = ["progress"]
|
21
|
+
|
22
|
+
vals = Minitest::ReportersPlugin::MAPPING.keys.join(",")
|
23
|
+
description = <<~DESC.strip
|
24
|
+
The reporters to use for test output as comma-seperated list.
|
25
|
+
DESC
|
26
|
+
|
27
|
+
opts.on("-r", "--reporter #{vals}", Array, description) do |reporters|
|
28
|
+
reporters = options[:reporters] + reporters if reporters == ["junit"]
|
29
|
+
|
30
|
+
is_subset = (reporters - Minitest::ReportersPlugin::MAPPING.keys).empty?
|
31
|
+
raise OptionParser::InvalidArgument, "Invalid reporter: #{reporters.join(", ")}" unless is_subset
|
32
|
+
|
33
|
+
options[:reporters] = reporters
|
34
|
+
end
|
35
|
+
end
|
36
|
+
|
37
|
+
def plugin_reporters_init(options)
|
38
|
+
return if options[:reporters].nil? || options[:reporters].empty?
|
39
|
+
|
40
|
+
require "minitest/reporters"
|
41
|
+
|
42
|
+
reporters = options[:reporters].map do |reporter|
|
43
|
+
ReportersPlugin::MAPPING.fetch(reporter).call(options)
|
44
|
+
end.compact
|
45
|
+
|
46
|
+
Minitest::Reporters.use!(reporters, ENV, Minitest.backtrace_filter) unless reporters.empty?
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
@@ -0,0 +1,95 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
module Minitest
|
4
|
+
class << self
|
5
|
+
def plugin_superdiff_options(opts, options)
|
6
|
+
opts.on("--no-superdiff", "Disable superdiff") do
|
7
|
+
options[:no_superdiff] = true
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
def plugin_superdiff_init(options)
|
12
|
+
return if options[:no_superdiff]
|
13
|
+
|
14
|
+
require "super_diff"
|
15
|
+
Minitest::Assertions.prepend(SuperdiffPlugin)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
module SuperdiffPlugin
|
20
|
+
module Helpers
|
21
|
+
extend self
|
22
|
+
def inspect_styled(obj, style, prefix: nil)
|
23
|
+
obj = SuperDiff.inspect_object(obj, as_lines: false)
|
24
|
+
SuperDiff::Core::Helpers.style(style, "#{prefix}#{obj}")
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def diff(expected, actual)
|
29
|
+
SuperDiff::EqualityMatchers::Main.call(expected: expected, actual: actual)
|
30
|
+
end
|
31
|
+
|
32
|
+
def mu_pp(obj)
|
33
|
+
SuperDiff.inspect_object(obj, as_lines: false)
|
34
|
+
end
|
35
|
+
|
36
|
+
def assert_includes(collection, item, message = nil)
|
37
|
+
super
|
38
|
+
rescue Minitest::Assertion => e
|
39
|
+
raise if message
|
40
|
+
|
41
|
+
exception = Minitest::Assertion.new(AssertIncludesMessage.new(collection: collection, item: item))
|
42
|
+
exception.set_backtrace(e.backtrace)
|
43
|
+
raise exception
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
class AssertIncludesMessage
|
48
|
+
include SuperdiffPlugin::Helpers
|
49
|
+
|
50
|
+
def initialize(collection:, item:)
|
51
|
+
@collection = collection
|
52
|
+
@item = item
|
53
|
+
end
|
54
|
+
|
55
|
+
def to_s
|
56
|
+
return @to_s if defined?(@to_s)
|
57
|
+
|
58
|
+
content = if (diff = optional_diff)
|
59
|
+
collection = inspect_styled(@collection, :expected)
|
60
|
+
item = inspect_styled(@item, :actual)
|
61
|
+
|
62
|
+
<<~OUTPUT.strip
|
63
|
+
Expected #{collection} to include #{item}, but it did not.
|
64
|
+
|
65
|
+
#{diff}
|
66
|
+
OUTPUT
|
67
|
+
else
|
68
|
+
expected = inspect_styled(@collection, :expected, prefix: " Collection: ")
|
69
|
+
actual = inspect_styled(@item, :actual, prefix: "Missing item: ")
|
70
|
+
|
71
|
+
<<~OUTPUT.strip
|
72
|
+
Expected collection to include item but it did not.
|
73
|
+
|
74
|
+
#{expected}
|
75
|
+
#{actual}
|
76
|
+
OUTPUT
|
77
|
+
end
|
78
|
+
|
79
|
+
@to_s ||= content
|
80
|
+
end
|
81
|
+
|
82
|
+
def optional_diff
|
83
|
+
case @collection
|
84
|
+
when Array, Set
|
85
|
+
collection_with_item = @collection + [@item].flatten(1)
|
86
|
+
basic_diff(@collection, collection_with_item)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def basic_diff(expected, actual)
|
91
|
+
content = SuperDiff.diff(expected, actual)
|
92
|
+
"\nDiff:\n\n#{content}"
|
93
|
+
end
|
94
|
+
end
|
95
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pastel"
|
4
|
+
|
5
|
+
module Nicetest
|
6
|
+
class BacktraceFilter
|
7
|
+
BUNDLER_REGEX = %r{/bundler/gems}
|
8
|
+
GEMS_DEFAULT_DIR = Regexp.escape(Gem.default_dir)
|
9
|
+
GEMS_PATHS = Gem.path.map { |path| Regexp.new(Regexp.escape(path)) }
|
10
|
+
|
11
|
+
attr_reader :pastel
|
12
|
+
|
13
|
+
def initialize
|
14
|
+
@pastel = Cli::PASTEL
|
15
|
+
@silence_pattern = Regexp.union([
|
16
|
+
/sorbet-runtime/,
|
17
|
+
Regexp.escape(RbConfig::CONFIG["rubylibdir"]),
|
18
|
+
%r{/gems/bundler-\d+\.\d+\.\d+/lib/bundler},
|
19
|
+
%r{/gems/minitest-\d+\.\d+\.\d+/lib/minitest},
|
20
|
+
%r{/gems/minitest-reporters-\d+\.\d+\.\d+/lib/minitest},
|
21
|
+
%r{(bin|exe)/bundle:},
|
22
|
+
/internal:warning/,
|
23
|
+
])
|
24
|
+
@dim_pattern = Regexp.union([
|
25
|
+
GEMS_DEFAULT_DIR,
|
26
|
+
*GEMS_PATHS,
|
27
|
+
BUNDLER_REGEX,
|
28
|
+
])
|
29
|
+
end
|
30
|
+
|
31
|
+
def filter(backtrace)
|
32
|
+
bt = []
|
33
|
+
first_line = true
|
34
|
+
cwd = Dir.pwd
|
35
|
+
|
36
|
+
backtrace.each do |line|
|
37
|
+
silenced = silence?(line)
|
38
|
+
|
39
|
+
next if silenced && !first_line
|
40
|
+
|
41
|
+
first_line = false
|
42
|
+
|
43
|
+
bt << if silenced || (dim?(line) && !line.start_with?(cwd))
|
44
|
+
pastel.dim(trim_prefix(line))
|
45
|
+
else
|
46
|
+
colorize_line(trim_prefix(line))
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
bt
|
51
|
+
end
|
52
|
+
|
53
|
+
def dim?(line)
|
54
|
+
@dim_pattern.match?(line)
|
55
|
+
end
|
56
|
+
|
57
|
+
def silence?(line)
|
58
|
+
@silence_pattern.match?(line)
|
59
|
+
end
|
60
|
+
|
61
|
+
def trim_prefix(line)
|
62
|
+
line = line.delete_prefix(Dir.pwd + "/")
|
63
|
+
line.sub!(ENV["HOME"], "~")
|
64
|
+
line
|
65
|
+
end
|
66
|
+
|
67
|
+
def colorize_line(line)
|
68
|
+
match = line.match(/(.*):(\d+):in `(.*)'/)
|
69
|
+
|
70
|
+
if match
|
71
|
+
file = match[1]
|
72
|
+
line_number = match[2]
|
73
|
+
method = match[3]
|
74
|
+
|
75
|
+
colored_file = pastel.cyan(file)
|
76
|
+
colored_line_number = pastel.green(line_number)
|
77
|
+
colored_method = pastel.yellow(method)
|
78
|
+
|
79
|
+
"#{colored_file}:#{colored_line_number}:in `#{colored_method}'"
|
80
|
+
else
|
81
|
+
line
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
data/lib/nicetest/cli.rb
ADDED
@@ -0,0 +1,212 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pastel"
|
4
|
+
require "minitest"
|
5
|
+
require "optparse"
|
6
|
+
require "English"
|
7
|
+
require "set"
|
8
|
+
|
9
|
+
module Nicetest
|
10
|
+
class Cli
|
11
|
+
def initialize(argv)
|
12
|
+
@argv = argv
|
13
|
+
@logger = Logger.new($stderr)
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
cli_options = Opts.parse!(@argv)
|
18
|
+
if (dir = cli_options.cd)
|
19
|
+
run_in_directory(dir)
|
20
|
+
elsif File.exist?("Gemfile") && !File.read("Gemfile").include?("nicetest")
|
21
|
+
run_in_directory(".")
|
22
|
+
else
|
23
|
+
run_tests
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def run_tests
|
28
|
+
disable_autorun!
|
29
|
+
adjust_load_path!
|
30
|
+
|
31
|
+
args = @argv.dup
|
32
|
+
argv_test_files = select_file_args(args)
|
33
|
+
argv_test_files = glob_test_files("test") if argv_test_files.empty?
|
34
|
+
args -= argv_test_files
|
35
|
+
|
36
|
+
required_files = argv_test_files.flat_map do |file_or_dir|
|
37
|
+
require_path_or_dir(file_or_dir)
|
38
|
+
end
|
39
|
+
|
40
|
+
@logger.fatal!("no test files found") if required_files.compact.empty?
|
41
|
+
|
42
|
+
Minitest.run(args)
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def fetch_dep_loadpaths(gemspec, seen = {})
|
48
|
+
return [] if seen[gemspec.name]
|
49
|
+
|
50
|
+
seen[gemspec.name] = true
|
51
|
+
load_paths = gemspec.full_require_paths
|
52
|
+
|
53
|
+
gemspec.dependencies.each do |dep|
|
54
|
+
dep_spec = Gem.loaded_specs[dep.name]
|
55
|
+
next unless dep_spec
|
56
|
+
|
57
|
+
load_paths.concat(fetch_dep_loadpaths(dep_spec, seen))
|
58
|
+
end
|
59
|
+
|
60
|
+
load_paths
|
61
|
+
end
|
62
|
+
|
63
|
+
def run_in_directory(input_dir)
|
64
|
+
dir = File.expand_path(input_dir)
|
65
|
+
dir.delete_suffix!("/") # remove trailing slash
|
66
|
+
|
67
|
+
chdir = if input_dir != "."
|
68
|
+
->(&blk) do
|
69
|
+
@logger.info("changing directory to #{dir}")
|
70
|
+
Dir.chdir(dir, &blk)
|
71
|
+
end
|
72
|
+
else
|
73
|
+
->(&blk) { blk.call }
|
74
|
+
end
|
75
|
+
|
76
|
+
loadpaths = fetch_dep_loadpaths(Gem.loaded_specs["nicetest"]).map { |path| "-I#{path}" }
|
77
|
+
requires = ["-rnicetest"]
|
78
|
+
args_with_removed_leading_path = @argv.map do |arg|
|
79
|
+
arg = arg.dup
|
80
|
+
arg.delete_prefix!(dir)
|
81
|
+
arg.delete_prefix!(File.expand_path(dir))
|
82
|
+
arg.delete_prefix!("/")
|
83
|
+
arg
|
84
|
+
end
|
85
|
+
|
86
|
+
run_proc = proc do
|
87
|
+
requires << "-rbundler/setup" if File.exist?("Gemfile")
|
88
|
+
|
89
|
+
cmd = [
|
90
|
+
RbConfig.ruby,
|
91
|
+
*loadpaths,
|
92
|
+
*requires,
|
93
|
+
"-e",
|
94
|
+
"exit(Nicetest::Cli.new(ARGV).run_tests)",
|
95
|
+
"--",
|
96
|
+
*args_with_removed_leading_path,
|
97
|
+
]
|
98
|
+
@logger.debug("running #{cmd.join(" ")}")
|
99
|
+
system(*cmd)
|
100
|
+
$CHILD_STATUS.exitstatus
|
101
|
+
end
|
102
|
+
|
103
|
+
chdir.call do
|
104
|
+
if defined?(Bundler)
|
105
|
+
Bundler.with_unbundled_env(&run_proc)
|
106
|
+
else
|
107
|
+
run_proc.call
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
def select_file_args(args)
|
113
|
+
args = args.dup
|
114
|
+
Minitest.instance_variable_set(:@extensions, Set.new) # Avoid double-loading plugins
|
115
|
+
Minitest.load_plugins unless args.delete("--no-plugins") || ENV["MT_NO_PLUGINS"]
|
116
|
+
# this will remove all options from the args array
|
117
|
+
temporarily_disable_optparse_callbacks { Minitest.process_args(args) }
|
118
|
+
args
|
119
|
+
end
|
120
|
+
|
121
|
+
def glob_test_files(dir)
|
122
|
+
Dir.glob("#{dir}/**{,/*/**}/*_test.rb")
|
123
|
+
end
|
124
|
+
|
125
|
+
def require_path_or_dir(path_or_dir)
|
126
|
+
if path_or_dir.end_with?(".rb")
|
127
|
+
[try_require(path_or_dir)]
|
128
|
+
else
|
129
|
+
Dir.glob("#{path_or_dir}/**{,/*/**}/*_test.rb").map do |f|
|
130
|
+
try_require(f)
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def try_require(path)
|
136
|
+
full_path = File.expand_path(path)
|
137
|
+
require full_path
|
138
|
+
true
|
139
|
+
rescue => e
|
140
|
+
bt = e.backtrace.first(10).map { |line| "\t#{PASTEL.dim(line)}" }.join("\n")
|
141
|
+
@logger.warn("could not require #{full_path} (#{e.class}: #{e.message})\n#{bt}")
|
142
|
+
nil
|
143
|
+
end
|
144
|
+
|
145
|
+
def disable_autorun!
|
146
|
+
Minitest.class_variable_set(:@@installed_at_exit, true) # rubocop:disable Style/ClassVars
|
147
|
+
end
|
148
|
+
|
149
|
+
def adjust_load_path!
|
150
|
+
$LOAD_PATH.unshift(File.expand_path("test"))
|
151
|
+
end
|
152
|
+
|
153
|
+
def temporarily_disable_optparse_callbacks(&blk)
|
154
|
+
OptionParser.class_eval do
|
155
|
+
def noop_callback!(*); end
|
156
|
+
|
157
|
+
original_callback = instance_method(:callback!)
|
158
|
+
define_method(:callback!, instance_method(:noop_callback!))
|
159
|
+
|
160
|
+
yield
|
161
|
+
|
162
|
+
define_method(:callback!, original_callback)
|
163
|
+
remove_method(:noop_callback!)
|
164
|
+
end
|
165
|
+
end
|
166
|
+
|
167
|
+
Opts = Struct.new(:cd) do
|
168
|
+
class << self
|
169
|
+
def parse!(argv)
|
170
|
+
old_officious = OptionParser::Officious.dup
|
171
|
+
OptionParser::Officious.clear
|
172
|
+
|
173
|
+
options = new
|
174
|
+
parser = OptionParser.new do |opts|
|
175
|
+
opts.banner = ""
|
176
|
+
opts.raise_unknown = false
|
177
|
+
opts.on("--cd=DIR", "Change directory before running tests") do |dir|
|
178
|
+
options[:cd] = dir
|
179
|
+
end
|
180
|
+
end
|
181
|
+
|
182
|
+
parser.parse!(argv)
|
183
|
+
OptionParser::Officious.replace(old_officious)
|
184
|
+
options
|
185
|
+
end
|
186
|
+
end
|
187
|
+
end
|
188
|
+
|
189
|
+
PASTEL = Pastel.new
|
190
|
+
|
191
|
+
BANNER = <<~BANNER
|
192
|
+
☀️ #{PASTEL.bold.cyan("nicetest")} #{PASTEL.dim.italic(Nicetest::VERSION)}
|
193
|
+
|
194
|
+
A minimalistic test runner for Minitest.
|
195
|
+
|
196
|
+
#{PASTEL.bold("Usage")}
|
197
|
+
|
198
|
+
nicetest [options] [files or directories]
|
199
|
+
|
200
|
+
#{PASTEL.bold("Examples")}
|
201
|
+
|
202
|
+
$ nicetest
|
203
|
+
$ nicetest test/models --reporter spec
|
204
|
+
$ nicetest test/models/user_test.rb:12
|
205
|
+
|
206
|
+
#{PASTEL.bold("Nicetest Options")}
|
207
|
+
--cd=DIR Change directory before running tests
|
208
|
+
#{PASTEL.bold("Minitest Options")}
|
209
|
+
|
210
|
+
BANNER
|
211
|
+
end
|
212
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "logger"
|
4
|
+
|
5
|
+
module Nicetest
|
6
|
+
class Logger
|
7
|
+
def initialize(io)
|
8
|
+
@pastel = Cli::PASTEL
|
9
|
+
@io = io
|
10
|
+
end
|
11
|
+
|
12
|
+
def warn(message)
|
13
|
+
log("warn ", message, color: :yellow)
|
14
|
+
end
|
15
|
+
|
16
|
+
def error(message)
|
17
|
+
log("error", message, color: :red)
|
18
|
+
end
|
19
|
+
|
20
|
+
def info(message)
|
21
|
+
log("info ", message, color: :cyan)
|
22
|
+
end
|
23
|
+
|
24
|
+
def debug(message)
|
25
|
+
if ENV["NICETEST_DEBUG"] == "1"
|
26
|
+
log("debug", message, color: :blue)
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
def fatal!(message)
|
31
|
+
error(message)
|
32
|
+
exit(1)
|
33
|
+
end
|
34
|
+
|
35
|
+
private
|
36
|
+
|
37
|
+
def log(level, message, color:)
|
38
|
+
@io.puts "#{target_level(level, color: color)} #{message}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def target_level(level, color:)
|
42
|
+
level = @pastel.bold.send(color, level)
|
43
|
+
logname = @pastel.dim.italic("nicetest →")
|
44
|
+
"#{level} #{logname}"
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
data/lib/nicetest.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require "pastel"
|
4
|
+
require "minitest/focus"
|
5
|
+
|
6
|
+
require_relative "nicetest/version"
|
7
|
+
require_relative "nicetest/logger"
|
8
|
+
require_relative "nicetest/cli"
|
9
|
+
require_relative "nicetest/backtrace_filter"
|
10
|
+
|
11
|
+
module Nicetest
|
12
|
+
class Error < StandardError; end
|
13
|
+
|
14
|
+
class << self
|
15
|
+
def logger
|
16
|
+
@logger ||= Logger.new($stderr)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data.tar.gz.sig
ADDED
Binary file
|
metadata
ADDED
@@ -0,0 +1,162 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: nicetest
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Ian Ker-Seymer
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain:
|
11
|
+
- |
|
12
|
+
-----BEGIN CERTIFICATE-----
|
13
|
+
MIIDODCCAiCgAwIBAgIBAjANBgkqhkiG9w0BAQsFADBCMRQwEgYDVQQDDAtpLmtl
|
14
|
+
cnNleW1lcjEVMBMGCgmSJomT8ixkARkWBWdtYWlsMRMwEQYKCZImiZPyLGQBGRYD
|
15
|
+
Y29tMB4XDTI0MDQxODA1MzY0N1oXDTI1MDQxODA1MzY0N1owQjEUMBIGA1UEAwwL
|
16
|
+
aS5rZXJzZXltZXIxFTATBgoJkiaJk/IsZAEZFgVnbWFpbDETMBEGCgmSJomT8ixk
|
17
|
+
ARkWA2NvbTCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBAMJ2pG+er4cP
|
18
|
+
PasxsMIKL9/tmLL4gh80EMuF3SCS0qZoh+Oo8dkvRYxW8NXdwEIcp3cCNgE+5G+J
|
19
|
+
TCMOVF8S15n1Z1P7xxXiXxa/BIofKKbtatVRngm14uR/6pjdkvLXqlrWdS57bNwv
|
20
|
+
7LtpzYVfDHfsl/qRWaEi4jq00PNCRSWjcva8teqswjBg8KlwGtyygpezPbVSWP8Y
|
21
|
+
vzWZmVF7fqRBXU78Ah0+pNOhslBXDTvI3xJdN4hQ3H7rLjpD/qxKWq/8o+Qvx6cX
|
22
|
+
dNZ3ugH/Pr3BAsqt4JFLXin9AK7PO9GDMH5JXJrUb+hAt2VNIZqpz9VlKA6BA0jN
|
23
|
+
eWGea+yCZkECAwEAAaM5MDcwCQYDVR0TBAIwADALBgNVHQ8EBAMCBLAwHQYDVR0O
|
24
|
+
BBYEFOkrF6hsocaIMOjR/K3JBzyXCLJPMA0GCSqGSIb3DQEBCwUAA4IBAQARHgco
|
25
|
+
yCxWUqN+HBGewmSB7T4oq6YS2FtU62K+GuWWCTqjl5byLKMXQW8HPDja9TC1My1m
|
26
|
+
2r0uDshtUvUjuB/Vfe9jVMTjnPBspHZYo0MRuMPc7owJaahjkqD3l7w9aa8Ci5aC
|
27
|
+
YU4Aj71Sc8s7YIxgHn/yIUdCe1yu6cC0+h+Ss9r/Yjr53NNXwjGQlDmH1eHcVQGZ
|
28
|
+
mEoVcZO1uWNtRPPsn1gfvKLPjRe5pclXQnGviS5DiH0Du+8QMxQGBJnYz2zSMW7G
|
29
|
+
Lvd35BNvZkhFzs9xfykhurpkT2TiP2F3ZFn9dwLXMFe41pwrtEYLIWhYi8mUG4Ek
|
30
|
+
6aR8M/tqIpChVV39
|
31
|
+
-----END CERTIFICATE-----
|
32
|
+
date: 2024-07-21 00:00:00.000000000 Z
|
33
|
+
dependencies:
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: minitest
|
36
|
+
requirement: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - ">="
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.0'
|
41
|
+
type: :runtime
|
42
|
+
prerelease: false
|
43
|
+
version_requirements: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '5.0'
|
48
|
+
- !ruby/object:Gem::Dependency
|
49
|
+
name: minitest-focus
|
50
|
+
requirement: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '1.4'
|
55
|
+
type: :runtime
|
56
|
+
prerelease: false
|
57
|
+
version_requirements: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '1.4'
|
62
|
+
- !ruby/object:Gem::Dependency
|
63
|
+
name: minitest-reporters
|
64
|
+
requirement: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '1.4'
|
69
|
+
type: :runtime
|
70
|
+
prerelease: false
|
71
|
+
version_requirements: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.4'
|
76
|
+
- !ruby/object:Gem::Dependency
|
77
|
+
name: optparse
|
78
|
+
requirement: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">="
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: 0.5.0
|
83
|
+
type: :runtime
|
84
|
+
prerelease: false
|
85
|
+
version_requirements: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: 0.5.0
|
90
|
+
- !ruby/object:Gem::Dependency
|
91
|
+
name: pastel
|
92
|
+
requirement: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.8'
|
97
|
+
type: :runtime
|
98
|
+
prerelease: false
|
99
|
+
version_requirements: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '0.8'
|
104
|
+
- !ruby/object:Gem::Dependency
|
105
|
+
name: super_diff
|
106
|
+
requirement: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '0.12'
|
111
|
+
type: :runtime
|
112
|
+
prerelease: false
|
113
|
+
version_requirements: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.12'
|
118
|
+
description: Configure Minitest with common plugins and options to make it even nicer
|
119
|
+
to use.
|
120
|
+
email:
|
121
|
+
- ian.kerseymer@shopify.com
|
122
|
+
executables:
|
123
|
+
- nicetest
|
124
|
+
extensions: []
|
125
|
+
extra_rdoc_files: []
|
126
|
+
files:
|
127
|
+
- LICENSE.txt
|
128
|
+
- README.md
|
129
|
+
- exe/nicetest
|
130
|
+
- lib/minitest/nicetest_plugin.rb
|
131
|
+
- lib/minitest/reporters_plugin.rb
|
132
|
+
- lib/minitest/superdiff_plugin.rb
|
133
|
+
- lib/nicetest.rb
|
134
|
+
- lib/nicetest/backtrace_filter.rb
|
135
|
+
- lib/nicetest/cli.rb
|
136
|
+
- lib/nicetest/logger.rb
|
137
|
+
- lib/nicetest/version.rb
|
138
|
+
homepage: https://github.com/ianks/nicetest
|
139
|
+
licenses:
|
140
|
+
- MIT
|
141
|
+
metadata:
|
142
|
+
allowed_push_host: https://rubygems.org
|
143
|
+
post_install_message:
|
144
|
+
rdoc_options: []
|
145
|
+
require_paths:
|
146
|
+
- lib
|
147
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: 3.0.0
|
152
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
153
|
+
requirements:
|
154
|
+
- - ">="
|
155
|
+
- !ruby/object:Gem::Version
|
156
|
+
version: '0'
|
157
|
+
requirements: []
|
158
|
+
rubygems_version: 3.5.9
|
159
|
+
signing_key:
|
160
|
+
specification_version: 4
|
161
|
+
summary: A slightly fancier configuration for Minitest
|
162
|
+
test_files: []
|
metadata.gz.sig
ADDED
Binary file
|