minitest-markdown 0.2.0.pre → 0.2.2.pre
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/CHANGELOG.md +11 -0
- data/README.md +26 -14
- data/lib/minitest/markdown/configuration.rb +13 -10
- data/lib/minitest/markdown/{error.rb → errors.rb} +1 -3
- data/lib/minitest/markdown/test_class.rb +35 -7
- data/lib/minitest/markdown/test_code_block.rb +10 -8
- data/lib/minitest/markdown/version.rb +1 -1
- data/lib/minitest/markdown.rb +1 -1
- data/lib/minitest/stub_chain.rb +6 -6
- metadata +2 -2
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 0a9a40aad00d3cf796ff92234e9677cc452131a2ca0ff09936d77de0155e1cd7
|
4
|
+
data.tar.gz: 57e2ce0e6b9b4a3752a621dde91cdf8e483dca82efa4eb6373a2de04f2175769
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a09e92d607c2484518f8f23fac73e709de766b62f41d17bdfeaccc4931293b2e00cbdf35a5ad4872be6aae6f4022a6579d38dc31c23a5581191c16c85924b596
|
7
|
+
data.tar.gz: 96fbb9758bfd397dcce743c0f76c9e78e6155b4fd5f4a299e9c64bb304503732565f4310b348032a43fb4febccab3e7eb79b1cc12aa3a21b82563fc2760d829d
|
data/CHANGELOG.md
CHANGED
@@ -1,5 +1,16 @@
|
|
1
1
|
## [Unreleased]
|
2
2
|
|
3
|
+
## [0.2.2.pre] - 2025-05-08
|
4
|
+
|
5
|
+
- Improve magic comment & hook comment parsing
|
6
|
+
- Fix bug with empty sting after magic comment
|
7
|
+
- Fix bug in config if Bundler undefined
|
8
|
+
|
9
|
+
## [0.2.1.pre] - 2025-05-07
|
10
|
+
|
11
|
+
- Fix issue with require in eval
|
12
|
+
- Fix bug in StubChain where instance was mutated after #call
|
13
|
+
|
3
14
|
## [0.2.0.pre] - 2025-05-06
|
4
15
|
|
5
16
|
- Add ability to pass any number of stubs to a test code block
|
data/README.md
CHANGED
@@ -21,6 +21,17 @@ If bundler is not being used to manage dependencies, install the gem by executin
|
|
21
21
|
gem install minitest-markdown
|
22
22
|
```
|
23
23
|
|
24
|
+
## Configuration
|
25
|
+
|
26
|
+
No configuation is required if you use Bundler. If not, set your `project_root` path using the setter method:
|
27
|
+
```ruby
|
28
|
+
@config = Markdown.config
|
29
|
+
# => instance_of Configuration
|
30
|
+
|
31
|
+
Configuration.instance_methods(false)
|
32
|
+
# => includes :project_root=
|
33
|
+
```
|
34
|
+
|
24
35
|
## Usage
|
25
36
|
|
26
37
|
### In your test class
|
@@ -106,11 +117,13 @@ Everything in the code blocks above runs as test code. [minitest-proveit](https:
|
|
106
117
|
It is possible to pass stubs to the generated tests. This is done using the stubs keyword. Hash keys represent the index of the test code block and the key is an instance of `StubChain`. `StubChain.stubproc` returns a procified stub which is called around the relevant test code. Zero or more of these reusable procs can be used to instantiate a StubChain object:
|
107
118
|
```ruby
|
108
119
|
class MarkdownTest < Minitest::Test
|
109
|
-
set_stubs_new = Minitest::StubChain.stubproc(Set, :new,
|
110
|
-
|
120
|
+
set_stubs_new = Minitest::StubChain.stubproc(Set, :new, Set.new) # returns a proc which stubs Set.new to return an empty set
|
121
|
+
set_stubs_size = Minitest::StubChain.stubproc(Set, :size, 42, any_instance: true) # uses the bundled minitest-stub_any_instance gem
|
111
122
|
|
112
123
|
stubs = {}
|
113
|
-
|
124
|
+
@stub_chain = Minitest::StubChain.new([set_stubs_new, set_stubs_size]) # initialized with zero or more stub procs
|
125
|
+
stubs[9] = @stub_chain
|
126
|
+
stubs[10] = @stub_chain # StubChain instances themseves may be reused
|
114
127
|
|
115
128
|
Markdown.generate_markdown_tests(self, stubs: stubs)
|
116
129
|
end
|
@@ -118,22 +131,21 @@ end
|
|
118
131
|
```
|
119
132
|
The 2 stubs above are demonstated in the following examples:
|
120
133
|
```ruby
|
121
|
-
# This is
|
122
|
-
Set.new([1, 'c', :s]).size # Here Set.new was stubbed to return an
|
134
|
+
# This is test_block9
|
135
|
+
Set.new([1, 'c', :s]).size # Here Set.new was stubbed to return an empty set and Set#size was stubbed to return 42
|
123
136
|
# => 42
|
124
137
|
```
|
125
|
-
|
126
|
-
## Configuration
|
127
|
-
|
128
|
-
No configuation is required if you use Bundler. If not, set your `project_root` path using the setter method:
|
138
|
+
Example showing the reuse of a StubChain:
|
129
139
|
```ruby
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
Configuration.instance_methods(false)
|
134
|
-
# => includes :project_root=
|
140
|
+
# This is test_block10
|
141
|
+
Set.new.size
|
142
|
+
# => 42
|
135
143
|
```
|
136
144
|
|
145
|
+
### Errors
|
146
|
+
|
147
|
+
All errors subclass `Markdown::Error`
|
148
|
+
|
137
149
|
## Development
|
138
150
|
|
139
151
|
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake test` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.
|
@@ -2,29 +2,32 @@
|
|
2
2
|
|
3
3
|
require 'pathname'
|
4
4
|
|
5
|
-
require_relative '
|
5
|
+
require_relative 'errors'
|
6
6
|
|
7
7
|
module Minitest
|
8
8
|
module Markdown
|
9
9
|
# configuration for gem
|
10
10
|
class Configuration
|
11
|
-
|
11
|
+
PROJECT_ROOT_UNDETERMINED = "Project root can't be determined, set with 'Markdown.config.project_root = /path'"
|
12
|
+
|
13
|
+
attr_writer :project_root
|
14
|
+
attr_accessor :prove_it
|
12
15
|
|
13
16
|
def initialize
|
14
|
-
@project_root = determine_project_root
|
17
|
+
@project_root = self.class.determine_project_root
|
15
18
|
@prove_it = true
|
16
19
|
end
|
17
20
|
|
18
|
-
def
|
19
|
-
|
21
|
+
def self.determine_project_root
|
22
|
+
Bundler.root if defined? Bundler
|
20
23
|
end
|
21
24
|
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
return Bundler.root if defined? Bundler
|
25
|
+
def project_root
|
26
|
+
@project_root || (raise Error, PROJECT_ROOT_UNDETERMINED)
|
27
|
+
end
|
26
28
|
|
27
|
-
|
29
|
+
def readme_path
|
30
|
+
project_root.join 'README.md'
|
28
31
|
end
|
29
32
|
end
|
30
33
|
end
|
@@ -3,6 +3,8 @@
|
|
3
3
|
require_relative '../assertions_extensions'
|
4
4
|
require_relative 'test_code_block'
|
5
5
|
|
6
|
+
$LOAD_PATH << '.' # fix #2
|
7
|
+
|
6
8
|
module Minitest
|
7
9
|
module Markdown
|
8
10
|
# knows how to build a markdown test
|
@@ -11,12 +13,15 @@ module Minitest
|
|
11
13
|
HOOK_METHODS = %i[before_all after_all around around_all].freeze # minitest-hooks extension
|
12
14
|
NON_TEST_METHODS = %i[setup teardown].freeze
|
13
15
|
|
16
|
+
BAD_KLASS = 'TestClass must be instantiated with Minitest::Test or subclass thereof'
|
17
|
+
BAD_PATH = 'Path does not exist or is not readable:'
|
18
|
+
|
14
19
|
attr_reader :klass, :path
|
15
20
|
|
16
21
|
def initialize(klass, path: nil)
|
17
|
-
@klass = klass
|
22
|
+
@klass = validate_class(klass)
|
18
23
|
@config = Markdown.config
|
19
|
-
@path = path
|
24
|
+
@path = validate_path(path)
|
20
25
|
@ruby_blocks = parse_ruby_blocks
|
21
26
|
end
|
22
27
|
|
@@ -28,6 +33,19 @@ module Minitest
|
|
28
33
|
|
29
34
|
private
|
30
35
|
|
36
|
+
def validate_class(klass)
|
37
|
+
raise ArgumentError, BAD_KLASS unless klass.respond_to?(:ancestors) && klass.ancestors.include?(Minitest::Test)
|
38
|
+
|
39
|
+
klass
|
40
|
+
end
|
41
|
+
|
42
|
+
def validate_path(path)
|
43
|
+
return @config.readme_path if path.nil?
|
44
|
+
raise ArgumentError, "#{BAD_PATH} #{path}" unless File.exist?(path) && File.file?(path)
|
45
|
+
|
46
|
+
path
|
47
|
+
end
|
48
|
+
|
31
49
|
def parse_ruby_blocks
|
32
50
|
File.new(path).read.scan(FENCED_BLOCK_REGEXP).flatten.map { |str| TestCodeBlock.new(str) }
|
33
51
|
end
|
@@ -52,13 +70,17 @@ module Minitest
|
|
52
70
|
end
|
53
71
|
|
54
72
|
def parse_non_test_method_block(type)
|
55
|
-
blocks = @ruby_blocks.map.select { |blk| blk
|
73
|
+
blocks = @ruby_blocks.map.select { |blk| parse_type(blk, type) }
|
56
74
|
return if blocks.empty?
|
57
|
-
raise Error
|
75
|
+
raise Error, "Multiple #{type} blocks are not allowed" if blocks.size > 1
|
58
76
|
|
59
77
|
@ruby_blocks.delete(blocks.first).fenced_block_str # hook method blocks can't be test methods
|
60
78
|
end
|
61
79
|
|
80
|
+
def parse_type(blk, type)
|
81
|
+
blk.fenced_block_str.lines.first.match?(/^\s*#\s*#{type}\s*\n$/)
|
82
|
+
end
|
83
|
+
|
62
84
|
def define_test_method(block, meth_index, stub_chain = nil)
|
63
85
|
stub_chain ||= StubChain.new
|
64
86
|
instance = self # scope
|
@@ -73,7 +95,7 @@ module Minitest
|
|
73
95
|
def evaluation_assertions(assertion_hash, bind)
|
74
96
|
ruby, assertion, args = TestCodeBlock::ASSERTION_KEYS.map { |key| assertion_hash[key] } # order dep
|
75
97
|
|
76
|
-
lmbda = -> {
|
98
|
+
lmbda = -> { kernel_eval(ruby) }
|
77
99
|
return unless assertion
|
78
100
|
return eval_with_block(bind, assertion, lmbda, args) if Assertions::WITH_BLOCK_EVAL.include? assertion
|
79
101
|
|
@@ -83,20 +105,26 @@ module Minitest
|
|
83
105
|
def eval_with_block(bind, assertion, actual, args)
|
84
106
|
return bind.receiver.send(assertion, &actual) if args.empty?
|
85
107
|
|
86
|
-
bind.receiver.send(assertion, *
|
108
|
+
bind.receiver.send(assertion, *kernel_eval(args), &actual)
|
87
109
|
end
|
88
110
|
|
89
111
|
def eval_without_block(bind, assertion, lmbda, args)
|
90
112
|
actual = lmbda.call
|
91
113
|
return bind.receiver.send(assertion, actual) if args == '[]'
|
92
114
|
|
93
|
-
expected, *rest =
|
115
|
+
expected, *rest = kernel_eval(args)
|
94
116
|
expected, actual = actual, expected if Assertions::EXPECTED_ACTUAL_REVERSED.include? assertion
|
95
117
|
kwargs = rest.last.is_a?(Hash) ? rest.last : nil
|
96
118
|
return bind.receiver.send(assertion, expected, actual, *rest) unless kwargs
|
97
119
|
|
98
120
|
bind.receiver.send(assertion, expected, actual, *rest, **kwargs) # assert_respond_to takes a kwarg..
|
99
121
|
end
|
122
|
+
|
123
|
+
def kernel_eval(args)
|
124
|
+
eval args # rubocop:disable Security/Eval
|
125
|
+
rescue SyntaxError
|
126
|
+
raise ArgumentError, "Invalid test code, failed to parse #{args}"
|
127
|
+
end
|
100
128
|
end
|
101
129
|
end
|
102
130
|
end
|
@@ -3,7 +3,7 @@
|
|
3
3
|
require 'minifyrb' # DEP
|
4
4
|
|
5
5
|
require_relative '../assertions_extensions' # for assert_truthy
|
6
|
-
require_relative '../markdown/
|
6
|
+
require_relative '../markdown/errors'
|
7
7
|
|
8
8
|
module Minitest
|
9
9
|
module Markdown
|
@@ -16,9 +16,9 @@ module Minitest
|
|
16
16
|
|
17
17
|
ASSERTION_KEYS = %i[ruby assertion test_args].freeze
|
18
18
|
|
19
|
-
MAGIC_COMMENT_DELIMITER = '
|
20
|
-
MAGIC_COMMENT_REGEXP =
|
21
|
-
MAGIC_COMMENT_SCAN_REGEXP =
|
19
|
+
MAGIC_COMMENT_DELIMITER = '=>'
|
20
|
+
MAGIC_COMMENT_REGEXP = /^\s*#\s*#{MAGIC_COMMENT_DELIMITER}.*$/
|
21
|
+
MAGIC_COMMENT_SCAN_REGEXP = /^\s*#\s*#{MAGIC_COMMENT_DELIMITER}(.*)$/ # strips delimiter prefix
|
22
22
|
|
23
23
|
attr_reader :fenced_block_str
|
24
24
|
|
@@ -41,10 +41,12 @@ module Minitest
|
|
41
41
|
private
|
42
42
|
|
43
43
|
def assertions_arr
|
44
|
-
code_strings.zip(magic_comments.map { |s| parse(s) }).map(&:flatten)
|
44
|
+
code_strings.zip(magic_comments.each_with_index.map { |s, i| parse(s, i) }).map(&:flatten)
|
45
45
|
end
|
46
46
|
|
47
|
-
def parse(magic_comment_string)
|
47
|
+
def parse(magic_comment_string, idx)
|
48
|
+
raise ArgumentError, "Block#{idx} magic comment missing assertion or value" if magic_comment_string.strip.empty?
|
49
|
+
|
48
50
|
assertion = parse_assertion(magic_comment_string)
|
49
51
|
return [DEFAULT_ASSERTION, "[#{magic_comment_string}]"] if assertion.nil?
|
50
52
|
return [assertion, '[]'] if magic_comment_string.split.size == 1
|
@@ -57,14 +59,14 @@ module Minitest
|
|
57
59
|
end
|
58
60
|
|
59
61
|
def magic_comments
|
60
|
-
@fenced_block_str.scan(MAGIC_COMMENT_SCAN_REGEXP).flatten
|
62
|
+
@fenced_block_str.scan(MAGIC_COMMENT_SCAN_REGEXP).flatten.map(&:strip)
|
61
63
|
end
|
62
64
|
|
63
65
|
def code_strings
|
64
66
|
@fenced_block_str.split(MAGIC_COMMENT_REGEXP).map do |str|
|
65
67
|
Minifyrb::Minifier.new(str).minify.strip
|
66
68
|
rescue SyntaxError
|
67
|
-
raise Error
|
69
|
+
raise Error, "Syntax error in:\n\n#{str}"
|
68
70
|
end
|
69
71
|
end
|
70
72
|
end
|
data/lib/minitest/markdown.rb
CHANGED
@@ -4,7 +4,7 @@ require 'minitest/stub_any_instance'
|
|
4
4
|
|
5
5
|
require_relative 'stub_chain'
|
6
6
|
require_relative 'markdown/configuration'
|
7
|
-
require_relative 'markdown/
|
7
|
+
require_relative 'markdown/errors'
|
8
8
|
require_relative 'markdown/version'
|
9
9
|
require_relative 'markdown/test_class'
|
10
10
|
|
data/lib/minitest/stub_chain.rb
CHANGED
@@ -20,8 +20,7 @@ module Minitest
|
|
20
20
|
|
21
21
|
def call(test_code_proc)
|
22
22
|
validate(test_code_proc)
|
23
|
-
stubs
|
24
|
-
call_recursive
|
23
|
+
call_recursive [*stubs, test_code_proc]
|
25
24
|
end
|
26
25
|
|
27
26
|
private
|
@@ -32,10 +31,11 @@ module Minitest
|
|
32
31
|
test_code_proc
|
33
32
|
end
|
34
33
|
|
35
|
-
def call_recursive
|
36
|
-
|
37
|
-
|
38
|
-
|
34
|
+
def call_recursive(call_chain)
|
35
|
+
prok = call_chain.shift
|
36
|
+
return prok.call if call_chain.empty?
|
37
|
+
|
38
|
+
prok.call { call_recursive(call_chain) }
|
39
39
|
end
|
40
40
|
end
|
41
41
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: minitest-markdown
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2.
|
4
|
+
version: 0.2.2.pre
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- MatzFan
|
@@ -77,7 +77,7 @@ files:
|
|
77
77
|
- lib/minitest/assertions_extensions.rb
|
78
78
|
- lib/minitest/markdown.rb
|
79
79
|
- lib/minitest/markdown/configuration.rb
|
80
|
-
- lib/minitest/markdown/
|
80
|
+
- lib/minitest/markdown/errors.rb
|
81
81
|
- lib/minitest/markdown/test_class.rb
|
82
82
|
- lib/minitest/markdown/test_code_block.rb
|
83
83
|
- lib/minitest/markdown/version.rb
|