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.
- checksums.yaml +4 -4
- data/CHANGELOG.adoc +32 -0
- data/README.adoc +17 -12
- data/Rakefile +28 -3
- data/features/README +1 -0
- data/features/fixtures/html-slim/Rakefile +15 -0
- data/features/fixtures/html-slim/examples/asciidoc/block_quote.adoc +12 -0
- data/features/fixtures/html-slim/examples/asciidoc/document.adoc +4 -0
- data/features/fixtures/html-slim/examples/asciidoc/inline_quoted.adoc +2 -0
- data/features/fixtures/html-slim/examples/html/block_quote.html +26 -0
- data/features/fixtures/html-slim/examples/html/document.html +7 -0
- data/features/fixtures/html-slim/templates/block_paragraph.html.slim +1 -0
- data/features/fixtures/html-slim/templates/block_quote.html.slim +7 -0
- data/features/fixtures/html-slim/templates/document.html.slim +13 -0
- data/features/fixtures/html-slim/templates/embedded.html.slim +3 -0
- data/features/fixtures/html-slim/templates/inline_quoted.html.slim +8 -0
- data/features/fixtures/html-slim/test/html_test.rb +6 -0
- data/features/fixtures/html-slim/test/test_helper.rb +5 -0
- data/features/generator_html.feature +171 -0
- data/features/step_definitions/doctest_steps.rb +5 -0
- data/features/support/env.rb +21 -0
- data/features/test_html.feature +59 -0
- data/lib/asciidoctor/doctest/asciidoc_renderer.rb +19 -14
- data/lib/asciidoctor/doctest/base_example.rb +11 -5
- data/lib/asciidoctor/doctest/base_examples_suite.rb +1 -1
- data/lib/asciidoctor/doctest/generator.rb +3 -1
- data/lib/asciidoctor/doctest/generator_task.rb +8 -5
- data/lib/asciidoctor/doctest/html/example.rb +1 -1
- data/lib/asciidoctor/doctest/html/examples_suite.rb +2 -2
- data/lib/asciidoctor/doctest/minitest_diffy.rb +1 -1
- data/lib/asciidoctor/doctest/test.rb +4 -1
- data/lib/asciidoctor/doctest/version.rb +1 -1
- data/spec/asciidoc_renderer_spec.rb +98 -0
- data/spec/base_example_spec.rb +4 -1
- data/spec/html/examples_suite_spec.rb +5 -5
- data/spec/minitest_diffy_spec.rb +57 -0
- data/spec/shared_examples/base_examples_suite.rb +0 -20
- data/spec/support/fakefs.rb +19 -0
- metadata +151 -74
@@ -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.
|
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:
|
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 ||=
|
41
|
+
@converter ||= NoFallbackTemplateConverter unless template_dirs.empty? || templates_fallback
|
42
42
|
|
43
43
|
template_dirs = Array.wrap(template_dirs).freeze
|
44
|
-
|
45
|
-
|
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
|
-
#
|
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]
|
56
|
+
# @return [String] converted input.
|
58
57
|
#
|
59
|
-
def
|
60
|
-
|
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.
|
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
|
-
#
|
74
|
-
|
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
|
-
#
|
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
|
116
|
-
# should override this method.
|
115
|
+
# @note (see #content_normalized)
|
117
116
|
#
|
118
|
-
# @return [String] a human-readable (formatted)
|
117
|
+
# @return [String] copy of the content in a human-readable (formatted)
|
118
|
+
# shape for pretty print.
|
119
119
|
#
|
120
|
-
def
|
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 [#
|
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 [#
|
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
|
42
|
+
# @return [Hash] options for Asciidoctor converter.
|
43
43
|
# @see AsciidocRenderer#initialize
|
44
|
-
attr_accessor :
|
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
|
-
@
|
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(
|
74
|
+
@renderer ||= AsciidocRenderer.new(converter_opts)
|
72
75
|
|
73
76
|
define
|
74
77
|
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.
|
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
|
101
|
+
create_example example.name, content: HtmlBeautifier.beautify(html), opts: opts
|
102
102
|
end
|
103
103
|
|
104
104
|
private
|
@@ -14,10 +14,13 @@ module Asciidoctor
|
|
14
14
|
|
15
15
|
##
|
16
16
|
# (see AsciidocRenderer#initialize)
|
17
|
-
def self.
|
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.
|
@@ -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
|
data/spec/base_example_spec.rb
CHANGED
@@ -2,7 +2,10 @@ describe DocTest::BaseExample do
|
|
2
2
|
|
3
3
|
subject(:o) { described_class.new ['foo', 'bar'] }
|
4
4
|
|
5
|
-
it
|
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(:
|
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(:
|
179
|
-
.with(input.content,
|
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(:
|
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
|