asciidoctor-doctest 1.5.0 → 1.5.1

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.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.adoc +32 -0
  3. data/README.adoc +17 -12
  4. data/Rakefile +28 -3
  5. data/features/README +1 -0
  6. data/features/fixtures/html-slim/Rakefile +15 -0
  7. data/features/fixtures/html-slim/examples/asciidoc/block_quote.adoc +12 -0
  8. data/features/fixtures/html-slim/examples/asciidoc/document.adoc +4 -0
  9. data/features/fixtures/html-slim/examples/asciidoc/inline_quoted.adoc +2 -0
  10. data/features/fixtures/html-slim/examples/html/block_quote.html +26 -0
  11. data/features/fixtures/html-slim/examples/html/document.html +7 -0
  12. data/features/fixtures/html-slim/templates/block_paragraph.html.slim +1 -0
  13. data/features/fixtures/html-slim/templates/block_quote.html.slim +7 -0
  14. data/features/fixtures/html-slim/templates/document.html.slim +13 -0
  15. data/features/fixtures/html-slim/templates/embedded.html.slim +3 -0
  16. data/features/fixtures/html-slim/templates/inline_quoted.html.slim +8 -0
  17. data/features/fixtures/html-slim/test/html_test.rb +6 -0
  18. data/features/fixtures/html-slim/test/test_helper.rb +5 -0
  19. data/features/generator_html.feature +171 -0
  20. data/features/step_definitions/doctest_steps.rb +5 -0
  21. data/features/support/env.rb +21 -0
  22. data/features/test_html.feature +59 -0
  23. data/lib/asciidoctor/doctest/asciidoc_renderer.rb +19 -14
  24. data/lib/asciidoctor/doctest/base_example.rb +11 -5
  25. data/lib/asciidoctor/doctest/base_examples_suite.rb +1 -1
  26. data/lib/asciidoctor/doctest/generator.rb +3 -1
  27. data/lib/asciidoctor/doctest/generator_task.rb +8 -5
  28. data/lib/asciidoctor/doctest/html/example.rb +1 -1
  29. data/lib/asciidoctor/doctest/html/examples_suite.rb +2 -2
  30. data/lib/asciidoctor/doctest/minitest_diffy.rb +1 -1
  31. data/lib/asciidoctor/doctest/test.rb +4 -1
  32. data/lib/asciidoctor/doctest/version.rb +1 -1
  33. data/spec/asciidoc_renderer_spec.rb +98 -0
  34. data/spec/base_example_spec.rb +4 -1
  35. data/spec/html/examples_suite_spec.rb +5 -5
  36. data/spec/minitest_diffy_spec.rb +57 -0
  37. data/spec/shared_examples/base_examples_suite.rb +0 -20
  38. data/spec/support/fakefs.rb +19 -0
  39. metadata +151 -74
@@ -0,0 +1,5 @@
1
+ require 'fileutils'
2
+
3
+ Given 'I do have a template-based HTML backend with DocTest' do
4
+ FileUtils.cp_r Dir.glob("#{FIXTURES_DIR}/html-slim/**"), TEMP_DIR
5
+ end
@@ -0,0 +1,21 @@
1
+ require 'aruba'
2
+ require 'aruba/cucumber'
3
+ require 'asciidoctor/doctest'
4
+ require 'fileutils'
5
+ require 'rspec/expectations'
6
+
7
+ Dir["#{__dir__}/../step_definitions/**/*.rb"].each { |file| require file }
8
+
9
+ PROJECT_DIR = File.expand_path('../../', __dir__)
10
+ FIXTURES_DIR = File.expand_path('../fixtures', __dir__)
11
+ TEMP_DIR = File.join(PROJECT_DIR, 'tmp/aruba')
12
+
13
+ Before do
14
+ FileUtils.mkdir_p TEMP_DIR
15
+ # overwrite Aruba's default temp directory location
16
+ @dirs = [TEMP_DIR]
17
+ end
18
+
19
+ After do
20
+ FileUtils.rm_rf(TEMP_DIR) if Dir.exist? TEMP_DIR
21
+ end
@@ -0,0 +1,59 @@
1
+ Feature: Testing a custom HTML backend
2
+
3
+ Background:
4
+ Given I do have a template-based HTML backend with DocTest
5
+
6
+ Scenario: Some examples do not match the expected output
7
+ When I run `bundle exec rake test`
8
+ Then the output should contain:
9
+ """
10
+ 1) Failure:
11
+ TestHtml :: block_quote : with_attribution:
12
+ Failing example..
13
+
14
+ <blockquote>A person who never made a mistake
15
+ <em>never</em>
16
+ tried anything new.</blockquote>
17
+ E <span>Albert Einstein</span>
18
+ A <div class="attribution">— Albert Einstein</div>
19
+ </div>
20
+ """
21
+ And the output should contain:
22
+ """
23
+ 2) Failure:
24
+ TestHtml :: document : title_with_author:
25
+ This example should fail..
26
+
27
+ <div id="header">
28
+ <h1>The Dangerous and Thrilling Documentation Chronicles</h1>
29
+ E <div id="author">Kismet Rainbow Chameleon</div>
30
+ A <div class="details"><span id="author">Kismet Rainbow Chameleon</span></div>
31
+ </div>
32
+ """
33
+ And the output should contain:
34
+ """
35
+ 5 runs, 3 assertions, 2 failures, 0 errors, 2 skips
36
+ """
37
+
38
+ Scenario: A necessary template is missing and fallback to the built-in converter is disabled
39
+ When I remove the file "templates/inline_quoted.html.slim"
40
+ And I run `bundle exec rake test`
41
+ Then the output should contain:
42
+ """
43
+ Could not find a custom template to handle template_name: inline_quoted
44
+ """
45
+ And the output should contain:
46
+ """
47
+ 1) Failure:
48
+ TestHtml :: block_quote : with_attribution:
49
+ Failing example..
50
+
51
+ <div class="quoteblock">
52
+ E <blockquote>A person who never made a mistake
53
+ E <em>never</em>
54
+ E tried anything new.</blockquote>
55
+ E <span>Albert Einstein</span>
56
+ A <blockquote>A person who never made a mistake --TEMPLATE NOT FOUND-- tried anything new.</blockquote>
57
+ A <div class="attribution">— Albert Einstein</div>
58
+ </div>
59
+ """
@@ -5,7 +5,7 @@ require 'asciidoctor/converter/template'
5
5
  module Asciidoctor
6
6
  module DocTest
7
7
  ##
8
- # This class is basically a wrapper for +Asciidoctor.render+ that allows to
8
+ # This class is basically a wrapper for +Asciidoctor.convert+ that allows to
9
9
  # preset and validate some common parameters.
10
10
  class AsciidocRenderer
11
11
 
@@ -33,45 +33,50 @@ module Asciidoctor
33
33
  # @raise [ArgumentError] if some path from the +template_dirs+ doesn't
34
34
  # exist or is not a directory.
35
35
  #
36
- def initialize(backend_name: nil, converter: nil, template_dirs: [],
36
+ def initialize(backend_name: '', converter: nil, template_dirs: [],
37
37
  templates_fallback: false)
38
38
 
39
39
  @backend_name = backend_name.freeze
40
40
  @converter = converter
41
- @converter ||= TemplateConverterAdapter unless template_dirs.empty? || templates_fallback
41
+ @converter ||= NoFallbackTemplateConverter unless template_dirs.empty? || templates_fallback
42
42
 
43
43
  template_dirs = Array.wrap(template_dirs).freeze
44
-
45
- unless template_dirs.all? { |path| Dir.exist? path }
46
- fail ArgumentError, "Templates directory '#{path}' doesn't exist!"
44
+ template_dirs.each do |path|
45
+ fail ArgumentError, "Templates directory '#{path}' doesn't exist!" unless Dir.exist? path
47
46
  end
48
47
  @template_dirs = template_dirs unless template_dirs.empty?
49
48
  end
50
49
 
51
50
  ##
52
- # Renders the given +text+ in AsciiDoc syntax with Asciidoctor using the
53
- # tested backend.
51
+ # Converts the given +text+ into AsciiDoc syntax with Asciidoctor using
52
+ # the tested backend.
54
53
  #
55
54
  # @param text [#to_s] the input text in AsciiDoc syntax.
56
55
  # @param opts [Hash] options to pass to Asciidoctor.
57
- # @return [String] rendered input.
56
+ # @return [String] converted input.
58
57
  #
59
- def render(text, opts = {})
60
- renderer_opts = {
58
+ def convert(text, opts = {})
59
+ converter_opts = {
61
60
  safe: :safe,
62
61
  backend: backend_name,
63
62
  converter: converter,
64
63
  template_dirs: template_dirs
65
64
  }.merge(opts)
66
65
 
67
- Asciidoctor.render(text.to_s, renderer_opts)
66
+ Asciidoctor.convert(text.to_s, converter_opts)
68
67
  end
68
+
69
+ # Alias for backward compatibility.
70
+ alias_method :render, :convert
69
71
  end
70
72
 
71
73
  ##
72
74
  # @private
73
- # Adapter for +Asciidoctor::Converter::TemplateConverter+.
74
- class TemplateConverterAdapter < SimpleDelegator
75
+ # TemplateConverter that doesn't fallback to a built-in converter when
76
+ # no template for a node is found.
77
+ class NoFallbackTemplateConverter < SimpleDelegator
78
+ # NOTE: It didn't work with subclass of TemplateConverter instead of
79
+ # delegator, I have no idea why.
75
80
 
76
81
  # Placeholder to be written in a rendered output in place of the node's
77
82
  # content that cannot be rendered due to missing template.
@@ -102,7 +102,7 @@ module Asciidoctor
102
102
 
103
103
  ##
104
104
  # @note The default implementation returns content as-is; subclasses
105
- # should override this method.
105
+ # should override this method.
106
106
  #
107
107
  # @return [String] copy of the content in a form that is suitable for
108
108
  # semantic comparison with another content.
@@ -112,15 +112,21 @@ module Asciidoctor
112
112
  end
113
113
 
114
114
  ##
115
- # @note The default implementation returns content as-is; subclasses
116
- # should override this method.
115
+ # @note (see #content_normalized)
117
116
  #
118
- # @return [String] a human-readable (formatted) version of the content.
117
+ # @return [String] copy of the content in a human-readable (formatted)
118
+ # shape for pretty print.
119
119
  #
120
- def to_s
120
+ def content_pretty
121
121
  content.dup
122
122
  end
123
123
 
124
+ ##
125
+ # @return (see #content_pretty)
126
+ def to_s
127
+ content_pretty
128
+ end
129
+
124
130
  ##
125
131
  # @param other the object to compare with.
126
132
  # @return [Boolean] +true+ if +self+ and +other+ equals in attributes
@@ -62,7 +62,7 @@ module Asciidoctor
62
62
  # @abstract
63
63
  # @param example [BaseExample] the input example to convert.
64
64
  # @param opts [Hash] the options to pass to a new example.
65
- # @param renderer [#render]
65
+ # @param renderer [#convert]
66
66
  # @return [BaseExample]
67
67
  # :nocov:
68
68
  def convert_example(example, opts, renderer)
@@ -17,7 +17,7 @@ module Asciidoctor
17
17
  # {BaseExamplesSuite} subclass to read the reference input
18
18
  # examples.
19
19
  #
20
- # @param renderer [#render]
20
+ # @param renderer [#convert]
21
21
  #
22
22
  # @param pattern [String] glob-like pattern to select examples to
23
23
  # (re)generate (see {BaseExample#name_match?}).
@@ -42,6 +42,8 @@ module Asciidoctor
42
42
  log["Unknown %s, doesn't exist in input examples!"]
43
43
  else
44
44
  rendered = output_suite.convert_example(input, output.opts, renderer)
45
+ rendered.desc = output.desc
46
+
45
47
  if output.empty?
46
48
  log['Generating %s', :magenta]
47
49
  updated << rendered
@@ -39,24 +39,27 @@ module Asciidoctor
39
39
  # (default: *:*).
40
40
  attr_accessor :pattern
41
41
 
42
- # @return [Hash] options for Asciidoctor renderer.
42
+ # @return [Hash] options for Asciidoctor converter.
43
43
  # @see AsciidocRenderer#initialize
44
- attr_accessor :renderer_opts
44
+ attr_accessor :converter_opts
45
45
 
46
46
  # @return [String] title of the task's description.
47
47
  attr_accessor :title
48
48
 
49
+ # Alias for backward compatibility.
50
+ alias_method :renderer_opts, :converter_opts
51
+
49
52
 
50
53
  ##
51
54
  # @param name [#to_sym] name of the task.
52
55
  # @yield The block to configure this task.
53
56
  def initialize(name)
54
57
  @name = name
55
- @examples_path = DocTest.examples_path
58
+ @examples_path = DocTest.examples_path.dup
56
59
  @force = false
57
60
  @input_suite = nil
58
61
  @output_suite = nil
59
- @renderer_opts = {}
62
+ @converter_opts = {}
60
63
  @pattern = '*:*'
61
64
  @title = "Generate testing examples #{pattern}#{" for #{name}" if name != :generate}."
62
65
 
@@ -68,7 +71,7 @@ module Asciidoctor
68
71
  end
69
72
 
70
73
  @input_suite ||= Asciidoc::ExamplesSuite.new(examples_path: @examples_path)
71
- @renderer ||= AsciidocRenderer.new(renderer_opts)
74
+ @renderer ||= AsciidocRenderer.new(converter_opts)
72
75
 
73
76
  define
74
77
  end
@@ -13,7 +13,7 @@ module Asciidoctor::DocTest
13
13
  Nokogiri::HTML.fragment(content).normalize!.to_s
14
14
  end
15
15
 
16
- def to_s
16
+ def content_pretty
17
17
  HtmlBeautifier.beautify content_normalized
18
18
  end
19
19
  end
@@ -81,7 +81,7 @@ module Asciidoctor::DocTest
81
81
  def convert_example(example, opts, renderer)
82
82
  header_footer = !!opts[:header_footer] || example.name.start_with?('document')
83
83
 
84
- html = renderer.render(example.to_s, header_footer: header_footer)
84
+ html = renderer.convert(example.content, header_footer: header_footer)
85
85
  html = parse_html(html, !header_footer)
86
86
 
87
87
  # When asserting inline examples, ignore paragraph "wrapper".
@@ -98,7 +98,7 @@ module Asciidoctor::DocTest
98
98
 
99
99
  html.normalize!
100
100
 
101
- create_example example.name, content: html.to_s, opts: opts
101
+ create_example example.name, content: HtmlBeautifier.beautify(html), opts: opts
102
102
  end
103
103
 
104
104
  private
@@ -66,7 +66,7 @@ module Diffy
66
66
  padding + line.chomp
67
67
  end
68
68
  end
69
- "\n" + ary.join("\n")
69
+ "\n" + ary.compact.join("\n")
70
70
  end
71
71
  end
72
72
  end
@@ -14,10 +14,13 @@ module Asciidoctor
14
14
 
15
15
  ##
16
16
  # (see AsciidocRenderer#initialize)
17
- def self.renderer_opts(**kwargs)
17
+ def self.converter_opts(**kwargs)
18
18
  @renderer = AsciidocRenderer.new(**kwargs)
19
19
  end
20
20
 
21
+ # Alias for backward compatibility.
22
+ alias_class_method :renderer_opts, :converter_opts
23
+
21
24
  ##
22
25
  # Generates tests for all the input/output examples.
23
26
  # When some output example is missing, it's reported as skipped test.
@@ -1,5 +1,5 @@
1
1
  module Asciidoctor
2
2
  module DocTest
3
- VERSION = '1.5.0'
3
+ VERSION = '1.5.1'
4
4
  end
5
5
  end
@@ -0,0 +1,98 @@
1
+ require 'fileutils'
2
+
3
+ describe DocTest::AsciidocRenderer do
4
+
5
+ subject { described_class }
6
+
7
+ it { is_expected.to have_method :convert, :render }
8
+
9
+
10
+ describe '#initialize' do
11
+
12
+ context 'with defaults' do
13
+ subject { described_class.new }
14
+ it { is_expected.to have_attributes backend_name: '', converter: nil, template_dirs: nil }
15
+ end
16
+
17
+ context 'with backend_name' do
18
+ subject { described_class.new(backend_name: 'html5') }
19
+ it { is_expected.to have_attributes backend_name: 'html5' }
20
+ end
21
+
22
+ context 'with template_dirs' do
23
+ include FakeFS::SpecHelpers
24
+
25
+ subject { described_class.new(template_dirs: template_dirs) }
26
+ let(:template_dirs) { ['/tmp/html5'] }
27
+
28
+ before { FileUtils.mkpath template_dirs[0] }
29
+
30
+ context 'that exists' do
31
+ it do
32
+ is_expected.to have_attributes(
33
+ template_dirs: template_dirs,
34
+ converter: DocTest::NoFallbackTemplateConverter
35
+ )
36
+ end
37
+
38
+ context 'and templates_fallback = true' do
39
+ subject { described_class.new(template_dirs: template_dirs, templates_fallback: true) }
40
+ it { is_expected.to have_attributes template_dirs: template_dirs, converter: nil }
41
+ end
42
+
43
+ context 'and custom converter' do
44
+ subject { described_class.new(template_dirs: template_dirs, converter: converter) }
45
+ let(:converter) { Asciidoctor::Converter::TemplateConverter }
46
+
47
+ it { is_expected.to have_attributes template_dirs: template_dirs, converter: converter }
48
+ end
49
+ end
50
+
51
+ context "that doesn't exist" do
52
+ let(:template_dirs) { ['/tmp/html5', '/tmp/revealjs'] }
53
+
54
+ it { expect { subject }.to raise_error ArgumentError }
55
+ end
56
+ end
57
+ end
58
+ end
59
+
60
+
61
+ describe DocTest::NoFallbackTemplateConverter do
62
+
63
+ subject(:delegator) { described_class.new('html5', template_dirs: ['/tmp/html5']) }
64
+
65
+ describe '#convert' do
66
+
67
+ let(:converter) { delegator.__getobj__ }
68
+ let(:node) { double('Node', node_name: 'block_foo') }
69
+
70
+ before do
71
+ expect(converter).to receive(:handles?).with('block_foo').and_return(handles)
72
+ end
73
+
74
+ context 'when template is not found' do
75
+ let(:handles) { false }
76
+
77
+ it 'returns a not found marker instead of converted node' do
78
+ expect(converter).to_not receive(:convert)
79
+ expect(delegator.convert node).to eq described_class::NOT_FOUND_MARKER
80
+ end
81
+
82
+ it 'prints a warning on stderr' do
83
+ expect { delegator.convert node }.to output(/Could not find a custom template/i).to_stderr
84
+ end
85
+ end
86
+
87
+ context 'when template is found' do
88
+ let(:handles) { true }
89
+
90
+ it 'delegates to the original #convert and returns result' do
91
+ expect(converter).to receive(:convert)
92
+ .with(node, 'block_foo', {}).and_return('allons-y!')
93
+
94
+ expect(delegator.convert node).to eq 'allons-y!'
95
+ end
96
+ end
97
+ end
98
+ end
@@ -2,7 +2,10 @@ describe DocTest::BaseExample do
2
2
 
3
3
  subject(:o) { described_class.new ['foo', 'bar'] }
4
4
 
5
- it { is_expected.to respond_to :group_name, :local_name, :content, :desc, :opts }
5
+ it do
6
+ is_expected.to respond_to :group_name, :local_name, :desc, :opts, :content,
7
+ :content_normalized, :content_pretty
8
+ end
6
9
 
7
10
  describe '#name' do
8
11
 
@@ -155,7 +155,7 @@ describe DocTest::HTML::ExamplesSuite do
155
155
  let(:input) { create_example 's:dummy', content: '*chunky* bacon' }
156
156
  let(:opts) { {dummy: 'value'} }
157
157
  let(:renderer) { double 'AsciidocRenderer' }
158
- let(:renderer_opts) { {header_footer: false} }
158
+ let(:converter_opts) { {header_footer: false} }
159
159
 
160
160
  subject(:result) { suite.convert_example input, opts, renderer }
161
161
 
@@ -175,8 +175,8 @@ describe DocTest::HTML::ExamplesSuite do
175
175
  end
176
176
 
177
177
  before do
178
- expect(renderer).to receive(:render)
179
- .with(input.content, renderer_opts).and_return(rendered)
178
+ expect(renderer).to receive(:convert)
179
+ .with(input.content, converter_opts).and_return(rendered)
180
180
  end
181
181
 
182
182
  it 'returns instance of HTML::Example' do
@@ -218,7 +218,7 @@ describe DocTest::HTML::ExamplesSuite do
218
218
 
219
219
  context 'with example named /^document.*/' do
220
220
  let(:input) { create_example 'document:dummy', content: '*chunky* bacon' }
221
- let(:renderer_opts) { {header_footer: true} }
221
+ let(:converter_opts) { {header_footer: true} }
222
222
 
223
223
  it 'renders content with :header_footer => true' do
224
224
  suite.convert_example input, {}, renderer
@@ -230,7 +230,7 @@ describe DocTest::HTML::ExamplesSuite do
230
230
  let(:rendered) { '<p><b>chunky</b> bacon</p>' }
231
231
 
232
232
  it 'returns content without top-level <p> tags' do
233
- expect(result.content).to eq '<b>chunky</b> bacon'
233
+ expect(result.content.gsub(/\s+/, ' ')).to eq '<b>chunky</b> bacon'
234
234
  end
235
235
 
236
236
  it 'does not add implicit include into returned example' do