minitest-markdown 0.2.1.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 959c342f3e4ec34238e2385e3578faf1d5133beb2f74d9b52d4ffc9ab0dc8d58
4
- data.tar.gz: 8b4bae7c2888040d6b2124fa2bd5d103c67405c883bf3de606f220e317912249
3
+ metadata.gz: 0a9a40aad00d3cf796ff92234e9677cc452131a2ca0ff09936d77de0155e1cd7
4
+ data.tar.gz: 57e2ce0e6b9b4a3752a621dde91cdf8e483dca82efa4eb6373a2de04f2175769
5
5
  SHA512:
6
- metadata.gz: e3d59e3fc4f77092cdd8dda4f3a0b8a8d79e1b73cbfc37186947efd6dbb0e35f508ab0d32da4f6a88d4c816d72370ff92b563e819c44073da4ea44404b946acb
7
- data.tar.gz: 2522d752e44a422f82be9ccb1a44c9ddec8346381d2cb0465990a7eee5b383e0a6b664c7f7b52c9ba860602c2a78411dc9f225c4a580bc287f65c26fbace05ad
6
+ metadata.gz: a09e92d607c2484518f8f23fac73e709de766b62f41d17bdfeaccc4931293b2e00cbdf35a5ad4872be6aae6f4022a6579d38dc31c23a5581191c16c85924b596
7
+ data.tar.gz: 96fbb9758bfd397dcce743c0f76c9e78e6155b4fd5f4a299e9c64bb304503732565f4310b348032a43fb4febccab3e7eb79b1cc12aa3a21b82563fc2760d829d
data/CHANGELOG.md CHANGED
@@ -1,5 +1,11 @@
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
+
3
9
  ## [0.2.1.pre] - 2025-05-07
4
10
 
5
11
  - Fix issue with require in eval
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,13 +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, []) # returns a proc which stubs Set.new to return an empty array
110
- array_stubs_size = Minitest::StubChain.stubproc(Array, :size, 42, any_instance: true) # uses the bundled minitest-stub_any_instance gem
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
- @stub_chain = Minitest::StubChain.new([set_stubs_new, array_stubs_size]) # initialized with zero or more stub procs
114
- stubs[8] = @stub_chain
115
- stubs[9] = @stub_chain # StubChain instances themseves may be reused
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
116
127
 
117
128
  Markdown.generate_markdown_tests(self, stubs: stubs)
118
129
  end
@@ -120,27 +131,20 @@ end
120
131
  ```
121
132
  The 2 stubs above are demonstated in the following examples:
122
133
  ```ruby
123
- # This is test_block8
124
- Set.new([1, 'c', :s]).size # Here Set.new was stubbed to return an Array and Array#size was stubbed to return 42
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
125
136
  # => 42
126
137
  ```
127
138
  Example showing the reuse of a StubChain:
128
139
  ```ruby
129
- # This is test_block9
130
- Set.new([]).size
140
+ # This is test_block10
141
+ Set.new.size
131
142
  # => 42
132
143
  ```
133
144
 
134
- ## Configuration
145
+ ### Errors
135
146
 
136
- No configuation is required if you use Bundler. If not, set your `project_root` path using the setter method:
137
- ```ruby
138
- @config = Markdown.config
139
- # => instance_of Configuration
140
-
141
- Configuration.instance_methods(false)
142
- # => includes :project_root=
143
- ```
147
+ All errors subclass `Markdown::Error`
144
148
 
145
149
  ## Development
146
150
 
@@ -2,29 +2,32 @@
2
2
 
3
3
  require 'pathname'
4
4
 
5
- require_relative 'error'
5
+ require_relative 'errors'
6
6
 
7
7
  module Minitest
8
8
  module Markdown
9
9
  # configuration for gem
10
10
  class Configuration
11
- attr_accessor :project_root, :prove_it
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 readme_path
19
- project_root.join 'README.md'
21
+ def self.determine_project_root
22
+ Bundler.root if defined? Bundler
20
23
  end
21
24
 
22
- private
23
-
24
- def determine_project_root
25
- return Bundler.root if defined? Bundler
25
+ def project_root
26
+ @project_root || (raise Error, PROJECT_ROOT_UNDETERMINED)
27
+ end
26
28
 
27
- raise Error::MarkdownError, "Project root can't be determined, set with 'Config.project_root = /root/path'"
29
+ def readme_path
30
+ project_root.join 'README.md'
28
31
  end
29
32
  end
30
33
  end
@@ -2,8 +2,6 @@
2
2
 
3
3
  module Minitest
4
4
  module Markdown
5
- module Error
6
- class MarkdownError < StandardError; end
7
- end
5
+ class Error < StandardError; end
8
6
  end
9
7
  end
@@ -13,12 +13,15 @@ module Minitest
13
13
  HOOK_METHODS = %i[before_all after_all around around_all].freeze # minitest-hooks extension
14
14
  NON_TEST_METHODS = %i[setup teardown].freeze
15
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
+
16
19
  attr_reader :klass, :path
17
20
 
18
21
  def initialize(klass, path: nil)
19
- @klass = klass
22
+ @klass = validate_class(klass)
20
23
  @config = Markdown.config
21
- @path = path || @config.readme_path
24
+ @path = validate_path(path)
22
25
  @ruby_blocks = parse_ruby_blocks
23
26
  end
24
27
 
@@ -30,6 +33,19 @@ module Minitest
30
33
 
31
34
  private
32
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
+
33
49
  def parse_ruby_blocks
34
50
  File.new(path).read.scan(FENCED_BLOCK_REGEXP).flatten.map { |str| TestCodeBlock.new(str) }
35
51
  end
@@ -54,13 +70,17 @@ module Minitest
54
70
  end
55
71
 
56
72
  def parse_non_test_method_block(type)
57
- blocks = @ruby_blocks.map.select { |blk| blk.fenced_block_str.lines.first == "# #{type}\n" }
73
+ blocks = @ruby_blocks.map.select { |blk| parse_type(blk, type) }
58
74
  return if blocks.empty?
59
- raise Error::MarkdownError, "Multiple #{type} blocks are not allowed" if blocks.size > 1
75
+ raise Error, "Multiple #{type} blocks are not allowed" if blocks.size > 1
60
76
 
61
77
  @ruby_blocks.delete(blocks.first).fenced_block_str # hook method blocks can't be test methods
62
78
  end
63
79
 
80
+ def parse_type(blk, type)
81
+ blk.fenced_block_str.lines.first.match?(/^\s*#\s*#{type}\s*\n$/)
82
+ end
83
+
64
84
  def define_test_method(block, meth_index, stub_chain = nil)
65
85
  stub_chain ||= StubChain.new
66
86
  instance = self # scope
@@ -75,7 +95,7 @@ module Minitest
75
95
  def evaluation_assertions(assertion_hash, bind)
76
96
  ruby, assertion, args = TestCodeBlock::ASSERTION_KEYS.map { |key| assertion_hash[key] } # order dep
77
97
 
78
- lmbda = -> { eval(ruby) } # rubocop:disable Security/Eval
98
+ lmbda = -> { kernel_eval(ruby) }
79
99
  return unless assertion
80
100
  return eval_with_block(bind, assertion, lmbda, args) if Assertions::WITH_BLOCK_EVAL.include? assertion
81
101
 
@@ -85,20 +105,26 @@ module Minitest
85
105
  def eval_with_block(bind, assertion, actual, args)
86
106
  return bind.receiver.send(assertion, &actual) if args.empty?
87
107
 
88
- bind.receiver.send(assertion, *eval(args), &actual) # rubocop:disable Security/Eval
108
+ bind.receiver.send(assertion, *kernel_eval(args), &actual)
89
109
  end
90
110
 
91
111
  def eval_without_block(bind, assertion, lmbda, args)
92
112
  actual = lmbda.call
93
113
  return bind.receiver.send(assertion, actual) if args == '[]'
94
114
 
95
- expected, *rest = eval(args) # rubocop:disable Security/Eval
115
+ expected, *rest = kernel_eval(args)
96
116
  expected, actual = actual, expected if Assertions::EXPECTED_ACTUAL_REVERSED.include? assertion
97
117
  kwargs = rest.last.is_a?(Hash) ? rest.last : nil
98
118
  return bind.receiver.send(assertion, expected, actual, *rest) unless kwargs
99
119
 
100
120
  bind.receiver.send(assertion, expected, actual, *rest, **kwargs) # assert_respond_to takes a kwarg..
101
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
102
128
  end
103
129
  end
104
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/error'
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 = /^#{MAGIC_COMMENT_DELIMITER}.*$/
21
- MAGIC_COMMENT_SCAN_REGEXP = /^#{MAGIC_COMMENT_DELIMITER}(.*)$/ # strips delimiter prefix
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::MarkdownError, "Syntax error in:\n\n#{str}"
69
+ raise Error, "Syntax error in:\n\n#{str}"
68
70
  end
69
71
  end
70
72
  end
@@ -2,6 +2,6 @@
2
2
 
3
3
  module Minitest
4
4
  module Markdown
5
- VERSION = '0.2.1.pre'
5
+ VERSION = '0.2.2.pre'
6
6
  end
7
7
  end
@@ -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/error'
7
+ require_relative 'markdown/errors'
8
8
  require_relative 'markdown/version'
9
9
  require_relative 'markdown/test_class'
10
10
 
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.1.pre
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/error.rb
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