asciidoctor-doctest 1.5.0 → 1.5.1

Sign up to get free protection for your applications and to get access to all the features.
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