minitest-markdown 0.0.0 → 0.0.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 9d404e86afc3911d3c33f3c6db07e8895fcba5dca70490ceebe23d7e419f18fd
4
- data.tar.gz: 6943203dde54269f4c4b4932d0aca354fc6a90b9531ca587956cdc6f90bb4686
3
+ metadata.gz: 6836d7576d3a208628f401e2660fac20d4288faf95c6513e76dd20a2a402fed2
4
+ data.tar.gz: 4f38166f7ff54f9a74391daafe8e698a7f0aabc57cf7a3797f292b3b8d85d524
5
5
  SHA512:
6
- metadata.gz: 8975c4e35527de62d85c8e92298fe67789ae74e1dcec8b39f2d3edf0527d78ea40ab995b8a08c6e5c303acb2b9bcb2957986a24d225a0f6003e438efdee46671
7
- data.tar.gz: 92578fe079a4cf4d90e5b74a3dc629b0517dbae6bdd0a08c236d9ce0f2b6ddb7fa36f380a315165006e2a3e5637b3d8bca5604d52124b0a2275c44bc26880695
6
+ metadata.gz: 393bc3aff42df19c5cfd10c2edddb64242798765d30aed3185e44be621547e0ed93f495113cca4082e388ab20f2da213f4f5ad4411b17f6117b1421c531d066a
7
+ data.tar.gz: a30d096c73f2064311cb2d729305fe5cc1d00cca871575fb75783d8046d28f5fc325d92882ccb886834358ed6a586f36eb397439829fba22f4d875c2f6e61998
data/CHANGELOG.md CHANGED
@@ -1,34 +1,5 @@
1
1
  ## [Unreleased]
2
2
 
3
- ## [0.0.0] - 2025-05-10
4
-
5
- - Fix bug in TestCodeBlock error message
6
- - Add binding to Kernel.eval fixes #1
7
- - pass kwargs to stub methods in StubProc
8
-
9
- ## [0.2.2.pre] - 2025-05-08
10
-
11
- - Improve magic comment & hook comment parsing
12
- - Fix bug with empty sting after magic comment
13
- - Fix bug in config if Bundler undefined
14
-
15
- ## [0.2.1.pre] - 2025-05-07
16
-
17
- - Fix issue with require in eval
18
- - Fix bug in StubChain where instance was mutated after #call
19
-
20
- ## [0.2.0.pre] - 2025-05-06
21
-
22
- - Add ability to pass any number of stubs to a test code block
23
-
24
- ### Breaking changes
25
- - StubChain class replaces Stubb module see https://gitlab.com/matzfan/minitest-markdown#stubbing
26
-
27
- ## [0.1.0.pre] - 2025-05-01
28
-
29
- - Add ability to pass stubs to test class
30
- - Add dependency; minitest-stub_any_instance
31
-
32
3
  ## [0.0.1.pre] - 2025-04-24
33
4
 
34
5
  - Bump minitest dep to >= 5.25.2
data/README.md CHANGED
@@ -5,11 +5,10 @@
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 functionality 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 functionaility in your README/wiki code blocks *before* you write your code!!
9
9
 
10
10
  ## Installation
11
11
 
12
-
13
12
  Add the gem to the application's Gemfile:
14
13
 
15
14
  ```bash
@@ -22,17 +21,6 @@ If bundler is not being used to manage dependencies, install the gem by executin
22
21
  gem install minitest-markdown
23
22
  ```
24
23
 
25
- ## Configuration
26
-
27
- No configuration is required if you use Bundler. If not, set your `project_root` path using the setter method:
28
- ```ruby
29
- @config = Minitest::Markdown.config
30
- # => instance_of Configuration
31
-
32
- Configuration.instance_methods(false)
33
- # => includes :project_root=
34
- ```
35
-
36
24
  ## Usage
37
25
 
38
26
  ### In your test class
@@ -42,20 +30,16 @@ To test the Ruby blocks in your README file, create file `test_readme.rb` (for e
42
30
  require 'minitest/autorun' # or in your test_helper
43
31
  require 'minitest/markdown' # ditto
44
32
 
45
- class ReadmeTest < Minitest::Test # or your own subclass of Minitest::Test
33
+ class ReadmeTest < MyTest # your own subclass of Minitest::Test
46
34
  Markdown.generate_markdown_tests(self)
47
35
  end
48
- # => truthy
36
+ # => nil
49
37
  ```
50
38
  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')`
51
39
 
52
40
  ### In your Markdown - magic comments become assertions
53
41
 
54
- 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.
55
-
56
- \*Any 'state' blocks are excluded from indexing - see State section below
57
-
58
- 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:
42
+ 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:
59
43
  ```ruby
60
44
  File.read 'test/fixtures/klass.rb'
61
45
  # => "class Klass\n def hello\n 'Hello Markdown!'\n end\nend"
@@ -69,15 +53,12 @@ Klass.new # inline comments are also ignored
69
53
  # The assertion and expected value are:-
70
54
  # => instance_of Klass
71
55
 
72
- # No keyword here, so the default assertion is used (assert_equal)
56
+ # No keywword here, so the default assertion is used (assert_equal)
73
57
  Klass.new.hello
74
58
  # => 'Hello Markdown!'
75
59
 
76
60
  Klass.hello
77
61
  # => raises NoMethodError
78
-
79
- self
80
- # => instance_of Markdown::TestClass
81
62
  ```
82
63
  Plain old `assert` has been aliased as `assert_truthy`, so when expecting a truthy value you should do this:
83
64
  ```ruby
@@ -91,7 +72,7 @@ For convenience, the assertion `assert_includes` has also been aliased so that i
91
72
  2
92
73
  # => included_in [1, 2, 3]
93
74
  ```
94
- 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:
75
+ 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:
95
76
  ```ruby
96
77
  22/7.0
97
78
  # => in_delta Math::PI, 0.01
@@ -101,96 +82,36 @@ To skip a test, use `skip`, as you would in a regular test:
101
82
  "some code which you don't want to test yet"
102
83
  # => skip 'give a reason for the skip here'
103
84
  ```
104
- Test failures will look like this - note the method name `test_block10` in this example:
105
- ```
106
- Minitest::Markdown::ReadmeTest#test_block10 [lib/minitest/markdown/test_class.rb:118]:
107
- Expected: 42
108
- Actual: 0
109
- ```
110
85
 
111
86
  ### State
112
87
 
113
- 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):
114
- ```ruby
115
- @instance_var
116
- # => 7
117
- ```
118
- 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.
119
-
88
+ 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.
120
89
  ```ruby
121
90
  # setup
122
91
 
123
- # do some setup task - or:-
124
- @instance_var = 7 # now available in all test method blocks, including the one above
125
- # => ignored
92
+ @instance_var = 42
126
93
  ```
94
+ The instance var set is now available to all test blocks (tests) in the Markdown file.
127
95
  ```ruby
128
- # teardown
129
-
130
- # do some teardown task
96
+ @instance_var
97
+ # => 42
131
98
  ```
132
99
  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.
133
100
 
134
101
 
135
- 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 ;-)
102
+ Everything in the code blocks above runs as test code. [minitest-proveit](https://github.com/seattlerb/minitest-proveit) would complain otherwise ;-)
136
103
 
137
- ### Mocks
138
-
139
- 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`:
140
- ```ruby
141
- @mymock = Minitest::Mock.new
142
- @mymock.expect(:puts, nil, ['Hello World!'])
143
-
144
- @mymock.puts 'Hello World!'
145
- # => mock @mymock
146
- ```
147
-
148
- ### Stubbing
149
-
150
- 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:
151
- ```ruby
152
- class MarkdownTest < Minitest::Test
153
- set_stubs_new = Minitest::StubChain.stubproc(Set, :new, []) # returns a proc which stubs Set.new to return an empty array
154
- array_stubs_size = Minitest::StubChain.stubproc(Array, :size, 42, any_instance: true) # uses the bundled minitest-stub_any_instance gem
155
-
156
- stub_chain = Minitest::StubChain.new([set_stubs_new]) # initialized with zero or more stub procs
157
- stub_chain.stubs << array_stubs_size # add another like this if need be
158
-
159
- stubs = {}
160
- stubs[10] = stub_chain
161
- stubs[11] = stub_chain # StubChain instances themselves may be reused
104
+ ## Configuration
162
105
 
163
- Markdown.generate_markdown_tests(self, stubs: stubs)
164
- end
165
- # => truthy
166
- ```
167
- The 2 stubs in the `StubChain` instance above are demonstrated in the following example:
106
+ No configuation is required if you use Bundler. If not, set your `project_root` path using the setter method:
168
107
  ```ruby
169
- # This is test_block10
170
- Set.new([1, 'c', :s]).size
171
- # => 42
108
+ @config = Markdown.config
109
+ # => instance_of Configuration
172
110
 
173
- # Because we added `stubs[10] = stub_chain` above, this is exactly equivalent to:
174
- #
175
- # def test_block10
176
- # Set.stub(:new, []) do
177
- # Array.stub_any_instance(:size, 42) do
178
- # assert_equal 42, Set.new([1, 'c', :s]).size
179
- # end
180
- # end
181
- # end
182
- ```
183
- Example showing the reuse of a StubChain:
184
- ```ruby
185
- # This is test_block11
186
- Set.new.size
187
- # => 42
111
+ Configuration.instance_methods(false)
112
+ # => includes :project_root=
188
113
  ```
189
114
 
190
- ### Errors
191
-
192
- All errors subclass `Markdown::Error`
193
-
194
115
  ## Development
195
116
 
196
117
  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.
@@ -199,7 +120,7 @@ To install this gem onto your local machine, run `bundle exec rake install`. To
199
120
 
200
121
  ## Contributing
201
122
 
202
- 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.
123
+ Bug reports and pull requests are welcome on GitHub at https://github.com/[USERNAME]/minitest-markdown. Please checkout a suitably named branch before submitting a PR.
203
124
 
204
125
  ## License
205
126
 
@@ -2,32 +2,29 @@
2
2
 
3
3
  require 'pathname'
4
4
 
5
- require_relative 'errors'
5
+ require_relative 'error'
6
6
 
7
7
  module Minitest
8
8
  module Markdown
9
9
  # configuration for gem
10
10
  class Configuration
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
11
+ attr_accessor :project_root, :prove_it
15
12
 
16
13
  def initialize
17
- @project_root = self.class.determine_project_root
14
+ @project_root = determine_project_root
18
15
  @prove_it = true
19
16
  end
20
17
 
21
- def self.determine_project_root
22
- Bundler.root if defined? Bundler
18
+ def readme_path
19
+ project_root.join 'README.md'
23
20
  end
24
21
 
25
- def project_root
26
- @project_root || (raise Error, PROJECT_ROOT_UNDETERMINED)
27
- end
22
+ private
28
23
 
29
- def readme_path
30
- project_root.join 'README.md'
24
+ def determine_project_root
25
+ return Bundler.root if defined? Bundler
26
+
27
+ raise Error::MarkdownError, "Project root can't be determined, set with 'Config.project_root = /root/path'"
31
28
  end
32
29
  end
33
30
  end
@@ -2,6 +2,8 @@
2
2
 
3
3
  module Minitest
4
4
  module Markdown
5
- class Error < StandardError; end
5
+ module Error
6
+ class MarkdownError < StandardError; end
7
+ end
6
8
  end
7
9
  end
@@ -1,88 +1,75 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  require_relative '../assertions_extensions'
4
- require_relative 'ruby_code_block'
5
-
6
- $LOAD_PATH << '.' # fix #2
4
+ require_relative 'test_code_block'
7
5
 
8
6
  module Minitest
9
7
  module Markdown
10
- # knows how to build a markdown test class, test methods and 'state' methods
8
+ # knows how to build a markdown test
11
9
  class TestClass
12
- FENCED_BLOCK_REGEXP = /^```ruby\n(.*?)\n```/m
13
-
14
- BAD_KLASS = 'TestClass must be instantiated with Minitest::Test or subclass thereof'
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
10
+ FENCED_BLOCK_REGEXP = /^```ruby\n(.*?)\n```/m # TODO: use Lexer/RDoc::Parser? which can deal with md comments
11
+ HOOK_METHODS = %i[before_all after_all around around_all].freeze # minitest-hooks extension
12
+ NON_TEST_METHODS = %i[setup teardown].freeze
20
13
 
21
14
  attr_reader :klass, :path
22
15
 
23
16
  def initialize(klass, path: nil)
24
- @klass = validate_class klass
25
- @path = validate_path path
26
- @state_methods_defined = []
17
+ @klass = klass
18
+ @config = Markdown.config
19
+ @path = path || @config.readme_path
20
+ @ruby_blocks = parse_ruby_blocks
27
21
  end
28
22
 
29
- def define_methods(stubs: {})
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
23
+ def define_methods
24
+ define_non_test_methods
25
+ @ruby_blocks.each_with_index { |block, i| define_test_method(block, i) }
26
+ nil
37
27
  end
38
28
 
39
29
  private
40
30
 
41
- def validate_class(klass)
42
- raise ArgumentError, BAD_KLASS unless klass.respond_to?(:ancestors) && klass.ancestors.include?(Minitest::Test)
43
-
44
- klass
31
+ def parse_ruby_blocks
32
+ File.new(path).read.scan(FENCED_BLOCK_REGEXP).flatten.map { |str| TestCodeBlock.new(str) }
45
33
  end
46
34
 
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
50
-
51
- pth
35
+ def define_non_test_methods
36
+ recognized_non_test_methods.each { |name| define_non_test_method(name, parse_non_test_method_block(name)) }
52
37
  end
53
38
 
54
- def parse_ruby_blocks
55
- File.new(path).read.scan(FENCED_BLOCK_REGEXP).flatten.map { |str| RubyCodeBlock.new(str) }
39
+ def recognized_non_test_methods
40
+ Minitest.const_defined?(:Hooks) ? NON_TEST_METHODS + HOOK_METHODS : NON_TEST_METHODS
56
41
  end
57
42
 
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
43
+ def define_non_test_method(type, block)
44
+ return unless block
61
45
 
62
46
  bind = binding
63
47
  klass.define_method(type) do
64
- super() if HOOK_METHODS_BEFORE.include? type
65
- bind.eval(block.fenced_block_str)
66
- super() if HOOK_METHODS_AFTER.include? type
48
+ super() if type == :before_all
49
+ bind.eval(block)
50
+ super() if %i[after_all around around_all].include? type
67
51
  end
68
- @state_methods_defined << type
69
52
  end
70
53
 
71
- def define_test_method(block, meth_index, stub_chain:)
72
- stub_chain ||= StubChain.new
54
+ def parse_non_test_method_block(type)
55
+ blocks = @ruby_blocks.map.select { |blk| blk.fenced_block_str.lines.first == "# #{type}\n" }
56
+ return if blocks.empty?
57
+ raise Error::MarkdownError, "Multiple #{type} blocks are not allowed" if blocks.size > 1
58
+
59
+ @ruby_blocks.delete(blocks.first).fenced_block_str # hook method blocks can't be test methods
60
+ end
61
+
62
+ def define_test_method(block, meth_index)
73
63
  instance = self # scope
74
64
  klass.define_method(:"test_block#{meth_index}") do
75
- test_code_proc = proc do
76
- block.assertions.each { |assertion_hash| instance.send(:evaluation_assertions, assertion_hash, binding) }
77
- end
78
- stub_chain.call(test_code_proc)
65
+ block.assertions.each { |assertion_hash| instance.send(:evaluation_assertions, assertion_hash, binding) }
79
66
  end
80
67
  end
81
68
 
82
69
  def evaluation_assertions(assertion_hash, bind)
83
- ruby, assertion, args = RubyCodeBlock::ASSERTION_KEYS.map { |key| assertion_hash[key] } # order dep
70
+ ruby, assertion, args = TestCodeBlock::ASSERTION_KEYS.map { |key| assertion_hash[key] } # order dep
84
71
 
85
- lmbda = -> { kernel_eval(ruby) }
72
+ lmbda = -> { eval(ruby) } # rubocop:disable Security/Eval
86
73
  return unless assertion
87
74
  return eval_with_block(bind, assertion, lmbda, args) if Assertions::WITH_BLOCK_EVAL.include? assertion
88
75
 
@@ -92,27 +79,20 @@ module Minitest
92
79
  def eval_with_block(bind, assertion, actual, args)
93
80
  return bind.receiver.send(assertion, &actual) if args.empty?
94
81
 
95
- bind.receiver.send(assertion, *kernel_eval(args), &actual)
82
+ bind.receiver.send(assertion, *eval(args), &actual) # rubocop:disable Security/Eval
96
83
  end
97
84
 
98
85
  def eval_without_block(bind, assertion, lmbda, args)
99
86
  actual = lmbda.call
100
87
  return bind.receiver.send(assertion, actual) if args == '[]'
101
88
 
102
- expected, *rest = kernel_eval(args)
89
+ expected, *rest = eval(args) # rubocop:disable Security/Eval
103
90
  expected, actual = actual, expected if Assertions::EXPECTED_ACTUAL_REVERSED.include? assertion
104
91
  kwargs = rest.last.is_a?(Hash) ? rest.last : nil
105
92
  return bind.receiver.send(assertion, expected, actual, *rest) unless kwargs
106
93
 
107
94
  bind.receiver.send(assertion, expected, actual, *rest, **kwargs) # assert_respond_to takes a kwarg..
108
95
  end
109
-
110
- def kernel_eval(str)
111
- bind = binding
112
- bind.eval str
113
- rescue SyntaxError
114
- raise ArgumentError, "Invalid test code, failed to parse #{str}"
115
- end
116
96
  end
117
97
  end
118
98
  end
@@ -1,14 +1,14 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'minifyrb'
3
+ require 'minifyrb' # DEP
4
4
 
5
5
  require_relative '../assertions_extensions' # for assert_truthy
6
- require_relative '../markdown/errors'
6
+ require_relative '../markdown/error'
7
7
 
8
8
  module Minitest
9
9
  module Markdown
10
- # A Ruby code block representing test code, with 0 or more assertions or a 'state' block
11
- class RubyCodeBlock
10
+ # A Ruby code block reprenting test code, with 0 or more assertions
11
+ class TestCodeBlock
12
12
  ASSERT_ = 'assert_'
13
13
  ASSERT_REGEXP = /\A#{ASSERT_}/
14
14
  SKIP = { skip: :skip }.freeze
@@ -16,35 +16,24 @@ module Minitest
16
16
 
17
17
  ASSERTION_KEYS = %i[ruby assertion test_args].freeze
18
18
 
19
- HOOK_METHODS = %i[before_all after_all around around_all].freeze # minitest-hooks extension
20
- STATE_METHODS = %i[setup teardown].freeze
21
- STATE_BLOCK_TYPES = Minitest.const_defined?(:Hooks) ? STATE_METHODS + HOOK_METHODS : STATE_METHODS
22
-
23
- MAGIC_COMMENT_REGEXP = /^\s*#\s*=>.*$/
24
- MAGIC_COMMENT_SCAN_REGEXP = /^\s*#\s*=>\s*(.*)$/ # strips delimiter prefix
25
-
26
- MISSING_ASSERTION_OR_VALUE = "Magic comment missing assertion or value. Something must follow '# =>'"
19
+ MAGIC_COMMENT_DELIMITER = '# => '
20
+ MAGIC_COMMENT_REGEXP = /^#{MAGIC_COMMENT_DELIMITER}.*$/
21
+ MAGIC_COMMENT_SCAN_REGEXP = /^#{MAGIC_COMMENT_DELIMITER}(.*)$/ # strips delimiter prefix
27
22
 
28
23
  attr_reader :fenced_block_str
29
24
 
30
25
  def self.asserts
31
- @asserts ||= Assertions.instance_methods.grep(ASSERT_REGEXP) - [:assert_send] # deprecated
26
+ Assertions.instance_methods.grep(ASSERT_REGEXP) - [:assert_send] # deprecated
32
27
  end
33
28
 
34
29
  def self.assertions_map
35
- @assertions_map ||= asserts.inject(SKIP) { |memo, m| memo.merge(m.to_s.split(ASSERT_)[1..].join.to_sym => m) }
30
+ asserts.inject({}) { |memo, m| memo.merge(m.to_s.split(ASSERT_)[1..].join.to_sym => m) }.merge(SKIP)
36
31
  end
37
32
 
38
33
  def initialize(fenced_block_str)
39
34
  @fenced_block_str = fenced_block_str
40
35
  end
41
36
 
42
- def type
43
- STATE_BLOCK_TYPES.each { |type| return type if fenced_block_str.lines.first&.match?(/^\s*#\s*#{type}\s*\n$/) }
44
-
45
- :test
46
- end
47
-
48
37
  def assertions
49
38
  assertions_arr.map { |arr| Hash[*ASSERTION_KEYS.zip(arr).flatten] }
50
39
  end
@@ -56,8 +45,6 @@ module Minitest
56
45
  end
57
46
 
58
47
  def parse(magic_comment_string)
59
- raise ArgumentError, MISSING_ASSERTION_OR_VALUE if magic_comment_string.strip.empty?
60
-
61
48
  assertion = parse_assertion(magic_comment_string)
62
49
  return [DEFAULT_ASSERTION, "[#{magic_comment_string}]"] if assertion.nil?
63
50
  return [assertion, '[]'] if magic_comment_string.split.size == 1
@@ -66,7 +53,7 @@ module Minitest
66
53
  end
67
54
 
68
55
  def parse_assertion(str)
69
- self.class.assertions_map[str.split.first.to_sym] # 'nil' => :assert_nil, but 'nil?' => nil
56
+ self.class.assertions_map[str.split.first.to_sym]
70
57
  end
71
58
 
72
59
  def magic_comments
@@ -77,7 +64,7 @@ module Minitest
77
64
  @fenced_block_str.split(MAGIC_COMMENT_REGEXP).map do |str|
78
65
  Minifyrb::Minifier.new(str).minify.strip
79
66
  rescue SyntaxError
80
- raise Error, "Syntax error in:\n\n#{str}"
67
+ raise Error::MarkdownError, "Syntax error in:\n\n#{str}"
81
68
  end
82
69
  end
83
70
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Minitest
4
4
  module Markdown
5
- VERSION = '0.0.0'
5
+ VERSION = '0.0.1.pre'
6
6
  end
7
7
  end
@@ -1,18 +1,13 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- require 'minitest/stub_any_instance'
4
-
5
- require_relative 'stub_chain'
6
3
  require_relative 'markdown/configuration'
7
- require_relative 'markdown/errors'
4
+ require_relative 'markdown/error'
8
5
  require_relative 'markdown/version'
9
6
  require_relative 'markdown/test_class'
10
7
 
11
8
  module Minitest
12
9
  # namespace
13
10
  module Markdown
14
- ARG_ERR = 'stubs keyword takes a hash. Keys are integers and values are StubChain instances'
15
-
16
11
  class << self
17
12
  def config
18
13
  @config ||= Configuration.new
@@ -22,12 +17,8 @@ module Minitest
22
17
  yield config
23
18
  end
24
19
 
25
- def generate_markdown_tests(klass, path: nil, stubs: {})
26
- raise ArgumentError, ARG_ERR unless stubs.is_a? Hash
27
- raise ArgumentError, ARG_ERR unless stubs.keys.all? { |o| o.instance_of? Integer }
28
- raise ArgumentError, ARG_ERR unless stubs.values.all? { |o| o.instance_of? StubChain }
29
-
30
- TestClass.new(klass, path: path).define_methods(stubs: stubs)
20
+ def generate_markdown_tests(klass, path: nil)
21
+ TestClass.new(klass, path: path).define_methods
31
22
  end
32
23
  end
33
24
  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.0.0
4
+ version: 0.0.1.pre
5
5
  platform: ruby
6
6
  authors:
7
7
  - MatzFan
@@ -49,20 +49,6 @@ dependencies:
49
49
  - - ">="
50
50
  - !ruby/object:Gem::Version
51
51
  version: 5.25.2
52
- - !ruby/object:Gem::Dependency
53
- name: minitest-stub_any_instance
54
- requirement: !ruby/object:Gem::Requirement
55
- requirements:
56
- - - "~>"
57
- - !ruby/object:Gem::Version
58
- version: '1.0'
59
- type: :runtime
60
- prerelease: false
61
- version_requirements: !ruby/object:Gem::Requirement
62
- requirements:
63
- - - "~>"
64
- - !ruby/object:Gem::Version
65
- version: '1.0'
66
52
  description: Generates tests for Ruby code blocks in any Markdown file.
67
53
  executables: []
68
54
  extensions: []
@@ -77,11 +63,10 @@ files:
77
63
  - lib/minitest/assertions_extensions.rb
78
64
  - lib/minitest/markdown.rb
79
65
  - lib/minitest/markdown/configuration.rb
80
- - lib/minitest/markdown/errors.rb
81
- - lib/minitest/markdown/ruby_code_block.rb
66
+ - lib/minitest/markdown/error.rb
82
67
  - lib/minitest/markdown/test_class.rb
68
+ - lib/minitest/markdown/test_code_block.rb
83
69
  - lib/minitest/markdown/version.rb
84
- - lib/minitest/stub_chain.rb
85
70
  - sig/minitest/markdown.rbs
86
71
  homepage: https://gitlab.com/matzfan/minitest-markdown
87
72
  licenses:
@@ -104,7 +89,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
104
89
  - !ruby/object:Gem::Version
105
90
  version: '0'
106
91
  requirements: []
107
- rubygems_version: 3.6.8
92
+ rubygems_version: 3.6.7
108
93
  specification_version: 4
109
94
  summary: Turn your README.md Ruby code blocks into testable code.
110
95
  test_files: []
@@ -1,41 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module Minitest
4
- # respresentaion of zero or more stub blocks around one or more test assertions
5
- class StubChain
6
- NOT_CALLABLE_ERR = 'StubChain#call takes a callable argument'
7
- MUST_NOT_CALL_A_BLOCK_ERR = 'StubChain#call takes a callable argument which must not call a block'
8
-
9
- attr_reader :stubs
10
-
11
- def initialize(stubs = [])
12
- @stubs = *stubs
13
- end
14
-
15
- def self.stubproc(klass, *args, any_instance: false, **kwargs)
16
- return proc { |&blk| klass.stub(*args, **kwargs) { blk.call } } unless any_instance
17
-
18
- proc { |&blk| klass.stub_any_instance(*args, **kwargs) { blk.call } }
19
- end
20
-
21
- def call(test_code_proc)
22
- validate(test_code_proc)
23
- call_recursive [*stubs, test_code_proc]
24
- end
25
-
26
- private
27
-
28
- def validate(test_code_proc)
29
- raise ArgumentError, NOT_CALLABLE_ERR unless test_code_proc.respond_to?(:call)
30
-
31
- test_code_proc
32
- end
33
-
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
- end
40
- end
41
- end