minitest-markdown 0.2.2.pre → 0.2.3

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: 0a9a40aad00d3cf796ff92234e9677cc452131a2ca0ff09936d77de0155e1cd7
4
- data.tar.gz: 57e2ce0e6b9b4a3752a621dde91cdf8e483dca82efa4eb6373a2de04f2175769
3
+ metadata.gz: fabeb5456de7296b1986c66822818a5fec9834e2c23e19b33a4c3c198e262ae6
4
+ data.tar.gz: bebbc7f77cd2443e91e58224888e5b69ab2d072b69e49c4cab86557cb0c3eeec
5
5
  SHA512:
6
- metadata.gz: a09e92d607c2484518f8f23fac73e709de766b62f41d17bdfeaccc4931293b2e00cbdf35a5ad4872be6aae6f4022a6579d38dc31c23a5581191c16c85924b596
7
- data.tar.gz: 96fbb9758bfd397dcce743c0f76c9e78e6155b4fd5f4a299e9c64bb304503732565f4310b348032a43fb4febccab3e7eb79b1cc12aa3a21b82563fc2760d829d
6
+ metadata.gz: 5b369fab40830b0a31951df1ddb8dc25868471f2766f81fd2c438bba7219921e45ef47e48d9c73b2224655d39a1005efd23e8c0a268481c3f9b0a3d628019ee2
7
+ data.tar.gz: 2789d00a505fa70ea7ce6c29352807c066951c4498862f4b1afe6190e568e3b7da31ce63b3a5baf74e51cd24759c183a432121cb1209d883faf672ec08a1dd13
data/CHANGELOG.md CHANGED
@@ -1,5 +1,19 @@
1
1
  ## [Unreleased]
2
2
 
3
+ ## [0.2.3] - 2025-05-22
4
+
5
+ - New release to catch up with 'pre' versioning
6
+
7
+ ## [0.0.1] - 2025-05-22
8
+
9
+ - Fix bug with Minitest::Hooks method definitions
10
+
11
+ ## [0.0.0] - 2025-05-10
12
+
13
+ - Fix bug in TestCodeBlock error message
14
+ - Add binding to Kernel.eval fixes #1
15
+ - pass kwargs to stub methods in StubProc
16
+
3
17
  ## [0.2.2.pre] - 2025-05-08
4
18
 
5
19
  - Improve magic comment & hook comment parsing
data/README.md CHANGED
@@ -5,10 +5,11 @@
5
5
 
6
6
  ## \_why?
7
7
 
8
- Document your Gem's usage, examples etc. with fully testable code! Better still, use your README as a BDD aid and specify functionaility in your README/wiki code blocks *before* you write your code!!
8
+ Document your Gem's usage, examples etc. with fully testable code! Better still, use your README as a BDD aid and specify functionality in your README/wiki code blocks *before* you write your code!!
9
9
 
10
10
  ## Installation
11
11
 
12
+
12
13
  Add the gem to the application's Gemfile:
13
14
 
14
15
  ```bash
@@ -23,9 +24,9 @@ gem install minitest-markdown
23
24
 
24
25
  ## Configuration
25
26
 
26
- No configuation is required if you use Bundler. If not, set your `project_root` path using the setter method:
27
+ No configuration is required if you use Bundler. If not, set your `project_root` path using the setter method:
27
28
  ```ruby
28
- @config = Markdown.config
29
+ @config = Minitest::Markdown.config
29
30
  # => instance_of Configuration
30
31
 
31
32
  Configuration.instance_methods(false)
@@ -40,17 +41,24 @@ To test the Ruby blocks in your README file, create file `test_readme.rb` (for e
40
41
  ```ruby
41
42
  require 'minitest/autorun' # or in your test_helper
42
43
  require 'minitest/markdown' # ditto
44
+ require 'minitest/hooks/test' # optional, see 'State' section below
43
45
 
44
46
  class ReadmeTest < Minitest::Test # or your own subclass of Minitest::Test
47
+ include Minitest::Hooks # optional, see 'State' section below
48
+
45
49
  Markdown.generate_markdown_tests(self)
46
50
  end
47
- # => nil
51
+ # => truthy
48
52
  ```
49
53
  To test Ruby blocks in another Markdown file, create another test file and pass the path to your Markdown file using the `:path` keyword arg i.e. `Markdown.generate_markdown_tests(self, path: '/path/to/your/markdown/file.md')`
50
54
 
51
55
  ### In your Markdown - magic comments become assertions
52
56
 
53
- Each Markdown file is represented by a single test class and each Ruby block in a file becomes a test method with zero or more assertions. The syntax used is `# => ` followed by an assertion keyword. Keywords may be one of the [Minitest "assert_" assertions](https://docs.seattlerb.org/minitest/Minitest/Assertions.html) less the "assert_" prefix (refutations are not implemented at this time). If the keyword is omitted, the default assertion; `assert_equal` is used. The actual value passed to the assertion is the result of the evaluation of the Ruby code above each magic comment. The following block (a single test) includes 3 assertions:
57
+ Each Markdown file is represented by a single test class and each Ruby block* in a file becomes a test method with zero or more assertions. Test methods are named according to their index; `test_block0`, `test_block1` etc.
58
+
59
+ \*Any 'state' blocks are excluded from indexing - see State section below
60
+
61
+ The syntax used for assertions is `# => ` followed by an assertion keyword. Keywords may be any one of the [Minitest "assert_" assertions](https://docs.seattlerb.org/minitest/Minitest/Assertions.html) less the "assert_" prefix (refutations are not implemented at this time). If the keyword is omitted, the default assertion; `assert_equal` is used. The actual value passed to the assertion is the result of the evaluation of the Ruby code above each magic comment. The following block (a single test) includes 6 assertions:
54
62
  ```ruby
55
63
  File.read 'test/fixtures/klass.rb'
56
64
  # => "class Klass\n def hello\n 'Hello Markdown!'\n end\nend"
@@ -64,12 +72,15 @@ Klass.new # inline comments are also ignored
64
72
  # The assertion and expected value are:-
65
73
  # => instance_of Klass
66
74
 
67
- # No keywword here, so the default assertion is used (assert_equal)
75
+ # No keyword here, so the default assertion is used (assert_equal)
68
76
  Klass.new.hello
69
77
  # => 'Hello Markdown!'
70
78
 
71
79
  Klass.hello
72
80
  # => raises NoMethodError
81
+
82
+ self
83
+ # => instance_of Markdown::TestClass
73
84
  ```
74
85
  Plain old `assert` has been aliased as `assert_truthy`, so when expecting a truthy value you should do this:
75
86
  ```ruby
@@ -83,7 +94,7 @@ For convenience, the assertion `assert_includes` has also been aliased so that i
83
94
  2
84
95
  # => included_in [1, 2, 3]
85
96
  ```
86
- Everything on the magic comment line after the keyword, or `# => `, if one is omitted, is evaluated as Ruby code. Note: **inline comments are NOT ALLOWED on magic comment lines**. Where an assertion takes multiple positional args, these are simply separated by commas. Note that the assertion keyword itself is **not an argument**. The syntax is as follows:
97
+ Everything on the magic comment line after the assertion keyword, or `# => ` if one is omitted, is evaluated as Ruby code. Note: **inline comments are NOT ALLOWED on magic comment lines**. Where an assertion takes multiple positional args, these are simply separated by commas. Note that the assertion keyword itself is **not an argument**. The syntax is as follows:
87
98
  ```ruby
88
99
  22/7.0
89
100
  # => in_delta Math::PI, 0.01
@@ -93,51 +104,95 @@ To skip a test, use `skip`, as you would in a regular test:
93
104
  "some code which you don't want to test yet"
94
105
  # => skip 'give a reason for the skip here'
95
106
  ```
107
+ Test failures will look like this - note the method name `test_block10` in this example:
108
+ ```
109
+ Minitest::Markdown::ReadmeTest#test_block10 [lib/minitest/markdown/test_class.rb:118]:
110
+ Expected: 42
111
+ Actual: 0
112
+ ```
96
113
 
97
114
  ### State
98
115
 
99
- Minitest's `setup` and `teardown` methods are generated by using the appropriate comment on the first line of a code block. Magic comments are ignored in such blocks, as these are not tests. E.g.
116
+ Instance vars are shared across test methods within a class, but as Minitest's default is to run tests in random order you may want to use a setup block in order to ensure a stored value is available to all test blocks (tests) in the Markdown file test class (see below):
117
+ ```ruby
118
+ @instance_var
119
+ # => 7
120
+
121
+ @before_all_instance_var # see hook methods below
122
+ # => 'foo'
123
+ ```
124
+ Minitest's `setup` and `teardown` methods are generated by using the appropriate comment on the first line of a code block. Assertion magic comments are ignored in such blocks, as these are not tests. E.g.
125
+
100
126
  ```ruby
101
127
  # setup
102
128
 
103
- @instance_var = 42
129
+ # do some setup task - or:-
130
+ @instance_var = 7 # now available in all test method blocks, including the one above
131
+ # => IGNORED
104
132
  ```
105
- The instance var set is now available to all test blocks (tests) in the Markdown file.
106
133
  ```ruby
107
- @instance_var
108
- # => 42
134
+ # teardown
135
+
136
+ # do some teardown task
109
137
  ```
110
- The hook methods defined in the [minitest-hooks](https://github.com/jeremyevans/minitest-hooks?tab=readme-ov-file#in-tests-minitesttest-) extension (`before_all`, `after_all`, `around` & `around_all`)are also available in this way if `minitest-hooks` is installed and `Minitest::Hooks` is included in your test class.
138
+ The hook methods defined in the [minitest-hooks](https://github.com/jeremyevans/minitest-hooks?tab=readme-ov-file#in-tests-minitesttest-) extension (`before_all`, `after_all`, `around` & `around_all`)are also available in this way if `minitest-hooks` is installed and `Minitest::Hooks` is included in your markdown test class. See the 'In your test class' section above for an example. You can now do this:
139
+
140
+ ```ruby
141
+ # before_all
142
+ @before_all_instance_var = 'foo'
143
+ ```
144
+
145
+ Everything in the Ruby code blocks above and below here runs as test code. [minitest-proveit](https://github.com/seattlerb/minitest-proveit) would complain otherwise ;-)
146
+
147
+ ### Mocks
111
148
 
149
+ Mocks use the "mock" keyword for [assert_mock](https://docs.seattlerb.org/minitest/Minitest/Assertions.html#method-i-assert_mock) in place of the equivalent `mock.verify`:
150
+ ```ruby
151
+ @mymock = Minitest::Mock.new
152
+ @mymock.expect(:puts, nil, ['Hello World!'])
112
153
 
113
- Everything in the code blocks above runs as test code. [minitest-proveit](https://github.com/seattlerb/minitest-proveit) would complain otherwise ;-)
154
+ @mymock.puts 'Hello World!'
155
+ # => mock @mymock
156
+ ```
114
157
 
115
- ## Stubbing
158
+ ### Stubbing
116
159
 
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:
160
+ 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. **Important: any 'state' blocks are ignored for test code block indexing**. The hash value must be an instance of `Minitest::StubChain` which holds a proc for each stub in the chain. For convenience `StubChain.stubproc` returns a suitable stub proc object which is called around the relevant test code. Zero or more of these reusable procs can be used to instantiate a StubChain object:
118
161
  ```ruby
119
162
  class MarkdownTest < Minitest::Test
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
163
+ set_stubs_new = Minitest::StubChain.stubproc(Set, :new, []) # returns a proc which stubs Set.new to return an empty array
164
+ array_stubs_size = Minitest::StubChain.stubproc(Array, :size, 42, any_instance: true) # uses the bundled minitest-stub_any_instance gem
165
+
166
+ stub_chain = Minitest::StubChain.new([set_stubs_new]) # initialized with zero or more stub procs
167
+ stub_chain.stubs << array_stubs_size # add another like this if need be
122
168
 
123
169
  stubs = {}
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
170
+ stubs[10] = stub_chain
171
+ stubs[11] = stub_chain # StubChain instances themselves may be reused
127
172
 
128
173
  Markdown.generate_markdown_tests(self, stubs: stubs)
129
174
  end
130
- # => nil
175
+ # => truthy
131
176
  ```
132
- The 2 stubs above are demonstated in the following examples:
177
+ The 2 stubs in the `StubChain` instance above are demonstrated in the following example:
133
178
  ```ruby
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
179
+ # This is test_block10
180
+ Set.new([1, 'c', :s]).size
136
181
  # => 42
182
+
183
+ # Because we added `stubs[10] = stub_chain` above, this is exactly equivalent to:
184
+ #
185
+ # def test_block10
186
+ # Set.stub(:new, []) do
187
+ # Array.stub_any_instance(:size, 42) do
188
+ # assert_equal 42, Set.new([1, 'c', :s]).size
189
+ # end
190
+ # end
191
+ # end
137
192
  ```
138
193
  Example showing the reuse of a StubChain:
139
194
  ```ruby
140
- # This is test_block10
195
+ # This is test_block11
141
196
  Set.new.size
142
197
  # => 42
143
198
  ```
@@ -154,7 +209,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
154
209
 
155
210
  ## Contributing
156
211
 
157
- Bug reports and pull requests are welcome on GitHub at https://gitlab.com/matzfan/minitest-markdown. Please checkout a suitably named branch before submitting a PR.
212
+ Bug reports and pull requests are welcome on GitLab at https://gitlab.com/matzfan/minitest-markdown. Please checkout a suitably named branch before submitting a PR.
158
213
 
159
214
  ## License
160
215
 
@@ -0,0 +1,82 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'minifyrb'
4
+
5
+ require_relative '../assertions_extensions' # for assert_truthy
6
+ require_relative '../markdown/errors'
7
+
8
+ module Minitest
9
+ module Markdown
10
+ # A Ruby code block representing test code, with 0 or more assertions or a 'state' block
11
+ class RubyCodeBlock
12
+ ASSERT_ = 'assert_'
13
+ ASSERT_REGEXP = /\A#{ASSERT_}/
14
+ SKIP = { skip: :skip }.freeze
15
+ DEFAULT_ASSERTION = :assert_equal
16
+
17
+ ASSERTION_KEYS = %i[ruby assertion test_args].freeze
18
+ STATE_BLOCK_TYPES = %i[setup teardown before_all after_all around around_all].freeze
19
+
20
+ MAGIC_COMMENT_REGEXP = /^\s*#\s*=>.*$/
21
+ MAGIC_COMMENT_SCAN_REGEXP = /^\s*#\s*=>\s*(.*)$/ # strips delimiter prefix
22
+
23
+ MISSING_ASSERTION_OR_VALUE = "Magic comment missing assertion or value. Something must follow '# =>'"
24
+
25
+ attr_reader :fenced_block_str
26
+
27
+ def self.asserts
28
+ @asserts ||= Assertions.instance_methods.grep(ASSERT_REGEXP) - [:assert_send] # deprecated
29
+ end
30
+
31
+ def self.assertions_map
32
+ @assertions_map ||= asserts.inject(SKIP) { |memo, m| memo.merge(m.to_s.split(ASSERT_)[1..].join.to_sym => m) }
33
+ end
34
+
35
+ def initialize(fenced_block_str)
36
+ @fenced_block_str = fenced_block_str
37
+ end
38
+
39
+ def type
40
+ STATE_BLOCK_TYPES.each { |type| return type if fenced_block_str.lines.first&.match?(/^\s*#\s*#{type}\s*\n$/) }
41
+
42
+ :test
43
+ end
44
+
45
+ def assertions
46
+ assertions_arr.map { |arr| Hash[*ASSERTION_KEYS.zip(arr).flatten] }
47
+ end
48
+
49
+ private
50
+
51
+ def assertions_arr
52
+ code_strings.zip(magic_comments.map { |s| parse(s) }).map(&:flatten)
53
+ end
54
+
55
+ def parse(magic_comment_string)
56
+ raise ArgumentError, MISSING_ASSERTION_OR_VALUE if magic_comment_string.strip.empty?
57
+
58
+ assertion = parse_assertion(magic_comment_string)
59
+ return [DEFAULT_ASSERTION, "[#{magic_comment_string}]"] if assertion.nil?
60
+ return [assertion, '[]'] if magic_comment_string.split.size == 1
61
+
62
+ [assertion, "[#{magic_comment_string.sub(/#{assertion.to_s.sub(ASSERT_, '')}\s+/, '')}]"]
63
+ end
64
+
65
+ def parse_assertion(str)
66
+ self.class.assertions_map[str.split.first.to_sym] # 'nil' => :assert_nil, but 'nil?' => nil
67
+ end
68
+
69
+ def magic_comments
70
+ @fenced_block_str.scan(MAGIC_COMMENT_SCAN_REGEXP).flatten
71
+ end
72
+
73
+ def code_strings
74
+ @fenced_block_str.split(MAGIC_COMMENT_REGEXP).map do |str|
75
+ Minifyrb::Minifier.new(str).minify.strip
76
+ rescue SyntaxError
77
+ raise Error, "Syntax error in:\n\n#{str}"
78
+ end
79
+ end
80
+ end
81
+ end
82
+ end
@@ -1,34 +1,39 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../assertions_extensions'
4
- require_relative 'test_code_block'
4
+ require_relative 'ruby_code_block'
5
5
 
6
6
  $LOAD_PATH << '.' # fix #2
7
7
 
8
8
  module Minitest
9
9
  module Markdown
10
- # knows how to build a markdown test
10
+ # knows how to build a markdown test class, test methods and 'state' methods
11
11
  class TestClass
12
- FENCED_BLOCK_REGEXP = /^```ruby\n(.*?)\n```/m # TODO: use Lexer/RDoc::Parser? which can deal with md comments
13
- HOOK_METHODS = %i[before_all after_all around around_all].freeze # minitest-hooks extension
14
- NON_TEST_METHODS = %i[setup teardown].freeze
12
+ FENCED_BLOCK_REGEXP = /^```ruby\n(.*?)\n```/m
15
13
 
16
14
  BAD_KLASS = 'TestClass must be instantiated with Minitest::Test or subclass thereof'
17
- BAD_PATH = 'Path does not exist or is not readable:'
15
+ BAD_PATH = 'Path does not exist, is not readable or is not a Markdown file:'
16
+ MD = '.md'
17
+
18
+ HOOK_METHODS_BEFORE = %i[before_all].freeze
19
+ HOOK_METHODS_AFTER = %i[after_all around around_all].freeze
18
20
 
19
21
  attr_reader :klass, :path
20
22
 
21
23
  def initialize(klass, path: nil)
22
- @klass = validate_class(klass)
23
- @config = Markdown.config
24
- @path = validate_path(path)
25
- @ruby_blocks = parse_ruby_blocks
24
+ @klass = validate_class klass
25
+ @path = validate_path path
26
+ @state_methods_defined = []
26
27
  end
27
28
 
28
29
  def define_methods(stubs: {})
29
- define_non_test_methods
30
- @ruby_blocks.each_with_index { |block, i| define_test_method(block, i, stubs[i]) }
31
- nil
30
+ i = 0
31
+ parse_ruby_blocks.each do |block|
32
+ next define_non_test_method(block) unless block.type == :test
33
+
34
+ define_test_method(block, i, stub_chain: stubs[i])
35
+ i += 1
36
+ end
32
37
  end
33
38
 
34
39
  private
@@ -39,49 +44,31 @@ module Minitest
39
44
  klass
40
45
  end
41
46
 
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)
47
+ def validate_path(pth)
48
+ return Markdown.config.readme_path if pth.nil?
49
+ raise ArgumentError, "#{BAD_PATH} #{pth}" unless File.exist?(pth) && File.file?(pth) && File.extname(pth) == MD
45
50
 
46
- path
51
+ pth
47
52
  end
48
53
 
49
54
  def parse_ruby_blocks
50
- File.new(path).read.scan(FENCED_BLOCK_REGEXP).flatten.map { |str| TestCodeBlock.new(str) }
51
- end
52
-
53
- def define_non_test_methods
54
- recognized_non_test_methods.each { |name| define_non_test_method(name, parse_non_test_method_block(name)) }
55
- end
56
-
57
- def recognized_non_test_methods
58
- Minitest.const_defined?(:Hooks) ? NON_TEST_METHODS + HOOK_METHODS : NON_TEST_METHODS
55
+ File.new(path).read.scan(FENCED_BLOCK_REGEXP).flatten.map { |str| RubyCodeBlock.new(str) }
59
56
  end
60
57
 
61
- def define_non_test_method(type, block)
62
- return unless block
58
+ def define_non_test_method(block)
59
+ type = block.type
60
+ raise Error, "State method: #{type} is already defined" if @state_methods_defined.include? type
63
61
 
64
62
  bind = binding
65
63
  klass.define_method(type) do
66
- super() if type == :before_all
67
- bind.eval(block)
68
- super() if %i[after_all around around_all].include? type
64
+ super() if HOOK_METHODS_BEFORE.include? type
65
+ bind.eval(block.fenced_block_str)
66
+ super() if HOOK_METHODS_AFTER.include? type
69
67
  end
68
+ @state_methods_defined << type
70
69
  end
71
70
 
72
- def parse_non_test_method_block(type)
73
- blocks = @ruby_blocks.map.select { |blk| parse_type(blk, type) }
74
- return if blocks.empty?
75
- raise Error, "Multiple #{type} blocks are not allowed" if blocks.size > 1
76
-
77
- @ruby_blocks.delete(blocks.first).fenced_block_str # hook method blocks can't be test methods
78
- end
79
-
80
- def parse_type(blk, type)
81
- blk.fenced_block_str.lines.first.match?(/^\s*#\s*#{type}\s*\n$/)
82
- end
83
-
84
- def define_test_method(block, meth_index, stub_chain = nil)
71
+ def define_test_method(block, meth_index, stub_chain:)
85
72
  stub_chain ||= StubChain.new
86
73
  instance = self # scope
87
74
  klass.define_method(:"test_block#{meth_index}") do
@@ -93,7 +80,7 @@ module Minitest
93
80
  end
94
81
 
95
82
  def evaluation_assertions(assertion_hash, bind)
96
- ruby, assertion, args = TestCodeBlock::ASSERTION_KEYS.map { |key| assertion_hash[key] } # order dep
83
+ ruby, assertion, args = RubyCodeBlock::ASSERTION_KEYS.map { |key| assertion_hash[key] } # order dep
97
84
 
98
85
  lmbda = -> { kernel_eval(ruby) }
99
86
  return unless assertion
@@ -120,10 +107,11 @@ module Minitest
120
107
  bind.receiver.send(assertion, expected, actual, *rest, **kwargs) # assert_respond_to takes a kwarg..
121
108
  end
122
109
 
123
- def kernel_eval(args)
124
- eval args # rubocop:disable Security/Eval
110
+ def kernel_eval(str)
111
+ bind = binding
112
+ bind.eval str
125
113
  rescue SyntaxError
126
- raise ArgumentError, "Invalid test code, failed to parse #{args}"
114
+ raise ArgumentError, "Invalid test code, failed to parse #{str}"
127
115
  end
128
116
  end
129
117
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Minitest
4
4
  module Markdown
5
- VERSION = '0.2.2.pre'
5
+ VERSION = '0.2.3'
6
6
  end
7
7
  end
@@ -12,10 +12,10 @@ module Minitest
12
12
  @stubs = *stubs
13
13
  end
14
14
 
15
- def self.stubproc(klass, method, ret_val, any_instance: false)
16
- return proc { |&blk| klass.stub(method, ret_val) { blk.call } } unless any_instance
15
+ def self.stubproc(klass, *args, any_instance: false, **kwargs)
16
+ return proc { |&blk| klass.stub(*args, **kwargs) { blk.call } } unless any_instance
17
17
 
18
- proc { |&blk| klass.stub_any_instance(method, ret_val) { blk.call } }
18
+ proc { |&blk| klass.stub_any_instance(*args, **kwargs) { blk.call } }
19
19
  end
20
20
 
21
21
  def call(test_code_proc)
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.2.pre
4
+ version: 0.2.3
5
5
  platform: ruby
6
6
  authors:
7
7
  - MatzFan
@@ -78,8 +78,8 @@ files:
78
78
  - lib/minitest/markdown.rb
79
79
  - lib/minitest/markdown/configuration.rb
80
80
  - lib/minitest/markdown/errors.rb
81
+ - lib/minitest/markdown/ruby_code_block.rb
81
82
  - lib/minitest/markdown/test_class.rb
82
- - lib/minitest/markdown/test_code_block.rb
83
83
  - lib/minitest/markdown/version.rb
84
84
  - lib/minitest/stub_chain.rb
85
85
  - sig/minitest/markdown.rbs
@@ -1,74 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'minifyrb' # DEP
4
-
5
- require_relative '../assertions_extensions' # for assert_truthy
6
- require_relative '../markdown/errors'
7
-
8
- module Minitest
9
- module Markdown
10
- # A Ruby code block reprenting test code, with 0 or more assertions
11
- class TestCodeBlock
12
- ASSERT_ = 'assert_'
13
- ASSERT_REGEXP = /\A#{ASSERT_}/
14
- SKIP = { skip: :skip }.freeze
15
- DEFAULT_ASSERTION = :assert_equal
16
-
17
- ASSERTION_KEYS = %i[ruby assertion test_args].freeze
18
-
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
-
23
- attr_reader :fenced_block_str
24
-
25
- def self.asserts
26
- Assertions.instance_methods.grep(ASSERT_REGEXP) - [:assert_send] # deprecated
27
- end
28
-
29
- def self.assertions_map
30
- asserts.inject({}) { |memo, m| memo.merge(m.to_s.split(ASSERT_)[1..].join.to_sym => m) }.merge(SKIP)
31
- end
32
-
33
- def initialize(fenced_block_str)
34
- @fenced_block_str = fenced_block_str
35
- end
36
-
37
- def assertions
38
- assertions_arr.map { |arr| Hash[*ASSERTION_KEYS.zip(arr).flatten] }
39
- end
40
-
41
- private
42
-
43
- def assertions_arr
44
- code_strings.zip(magic_comments.each_with_index.map { |s, i| parse(s, i) }).map(&:flatten)
45
- end
46
-
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
-
50
- assertion = parse_assertion(magic_comment_string)
51
- return [DEFAULT_ASSERTION, "[#{magic_comment_string}]"] if assertion.nil?
52
- return [assertion, '[]'] if magic_comment_string.split.size == 1
53
-
54
- [assertion, "[#{magic_comment_string.sub(/#{assertion.to_s.sub(ASSERT_, '')}\s+/, '')}]"]
55
- end
56
-
57
- def parse_assertion(str)
58
- self.class.assertions_map[str.split.first.to_sym]
59
- end
60
-
61
- def magic_comments
62
- @fenced_block_str.scan(MAGIC_COMMENT_SCAN_REGEXP).flatten.map(&:strip)
63
- end
64
-
65
- def code_strings
66
- @fenced_block_str.split(MAGIC_COMMENT_REGEXP).map do |str|
67
- Minifyrb::Minifier.new(str).minify.strip
68
- rescue SyntaxError
69
- raise Error, "Syntax error in:\n\n#{str}"
70
- end
71
- end
72
- end
73
- end
74
- end