asciidoctor-doctest 2.0.0.beta.4 → 2.0.0.beta.5

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 (48) hide show
  1. checksums.yaml +5 -5
  2. data/CHANGELOG.adoc +58 -0
  3. data/LICENSE +21 -0
  4. data/README.adoc +310 -0
  5. data/asciidoctor-doctest.gemspec +48 -0
  6. data/data/examples/asciidoc/embedded.adoc +15 -0
  7. data/data/examples/asciidoc/preamble.adoc +1 -2
  8. data/data/examples/asciidoc/section.adoc +9 -0
  9. data/data/examples/asciidoc/toc.adoc +21 -9
  10. data/lib/asciidoctor/doctest/html/converter.rb +1 -1
  11. data/lib/asciidoctor/doctest/io/asciidoc.rb +2 -3
  12. data/lib/asciidoctor/doctest/io/base.rb +1 -1
  13. data/lib/asciidoctor/doctest/io/xml.rb +1 -1
  14. data/lib/asciidoctor/doctest/test_reporter.rb +3 -3
  15. data/lib/asciidoctor/doctest/version.rb +1 -1
  16. metadata +20 -69
  17. data/doc/img/doctest-diag.odf +0 -0
  18. data/doc/img/doctest-diag.svg +0 -56
  19. data/doc/img/failing-test-term.gif +0 -0
  20. data/features/README +0 -1
  21. data/features/fixtures/html-slim/Rakefile +0 -9
  22. data/features/fixtures/html-slim/examples/asciidoc/document.adoc +0 -4
  23. data/features/fixtures/html-slim/examples/asciidoc/inline_quoted.adoc +0 -2
  24. data/features/fixtures/html-slim/examples/asciidoc/quote.adoc +0 -12
  25. data/features/fixtures/html-slim/examples/html/document.html +0 -7
  26. data/features/fixtures/html-slim/examples/html/quote.html +0 -26
  27. data/features/fixtures/html-slim/templates/document.html.slim +0 -13
  28. data/features/fixtures/html-slim/templates/embedded.html.slim +0 -3
  29. data/features/fixtures/html-slim/templates/inline_quoted.html.slim +0 -8
  30. data/features/fixtures/html-slim/templates/paragraph.html.slim +0 -1
  31. data/features/fixtures/html-slim/templates/quote.html.slim +0 -7
  32. data/features/generator_html.feature +0 -171
  33. data/features/step_definitions/doctest_steps.rb +0 -5
  34. data/features/support/env.rb +0 -24
  35. data/features/test_html.feature +0 -98
  36. data/spec/asciidoc_converter_spec.rb +0 -64
  37. data/spec/example_spec.rb +0 -180
  38. data/spec/factory_spec.rb +0 -46
  39. data/spec/html/converter_spec.rb +0 -137
  40. data/spec/html_normalizer_spec.rb +0 -70
  41. data/spec/io/asciidoc_spec.rb +0 -139
  42. data/spec/io/xml_spec.rb +0 -141
  43. data/spec/minitest_diffy_spec.rb +0 -59
  44. data/spec/no_fallback_template_converter_spec.rb +0 -38
  45. data/spec/shared_examples/base_examples.rb +0 -289
  46. data/spec/spec_helper.rb +0 -39
  47. data/spec/support/matchers.rb +0 -7
  48. data/spec/tester_spec.rb +0 -153
@@ -1,59 +0,0 @@
1
- require 'asciidoctor/doctest/minitest_diffy'
2
- require 'corefines'
3
-
4
- using Corefines::String::color
5
-
6
- describe Diffy::Format do
7
-
8
- describe '#minitest' do
9
-
10
- subject(:output) { input.minitest }
11
- let(:input) { [] }
12
-
13
- # This looks odd, but Diffy is actually using Format exactly like that.
14
- # https://github.com/samg/diffy/blob/6e1f1312/lib/diffy/diff.rb#L132
15
- before { input.extend described_class }
16
-
17
- it { expect(described_class).to have_method :minitest }
18
-
19
- context 'line starting with ---, or +++' do
20
- let(:input) { ['--- a/file.rb', '+++ b/file.rb'] }
21
-
22
- it 'discards the line' do
23
- expect(output.chomp).to be_empty
24
- end
25
- end
26
-
27
- context 'line with ""' do
28
- let(:input) { [''] }
29
-
30
- it 'discards this line' do
31
- expect(output.chomp).to be_empty
32
- end
33
- end
34
-
35
- context 'line starting with +' do
36
- let(:input) { ['+ <div><p>chunky bacon</p></div>'] }
37
-
38
- it 'replaces "+" with "A", adds padding and colour' do
39
- is_expected.to eq "\n" + 'A <div><p>chunky bacon</p></div>'.color(:red)
40
- end
41
- end
42
-
43
- context 'line starting with -' do
44
- let(:input) { ['- <p>chunky bacon</p>'] }
45
-
46
- it 'replaces "-" with "E", adds padding and colour' do
47
- is_expected.to eq "\n" + 'E <p>chunky bacon</p>'.color(:green)
48
- end
49
- end
50
-
51
- context 'normal lines' do
52
- let(:input) { ['Lorem ipsum dolor', ' sit amet'] }
53
-
54
- it 'returns the lines with padding' do
55
- is_expected.to eq "\n Lorem ipsum dolor\n sit amet"
56
- end
57
- end
58
- end
59
- end
@@ -1,38 +0,0 @@
1
- describe DocTest::NoFallbackTemplateConverter do
2
-
3
- subject(:delegator) { described_class.new('html5', template_dirs: ['/tmp/html5']) }
4
-
5
- describe '#convert' do
6
-
7
- let(:converter) { delegator.__getobj__ }
8
- let(:node) { double('Node', node_name: 'block_foo') }
9
-
10
- before do
11
- expect(converter).to receive(:handles?).with('block_foo').and_return(handles)
12
- end
13
-
14
- context 'when template is not found' do
15
- let(:handles) { false }
16
-
17
- it 'returns a not found marker instead of converted node' do
18
- expect(converter).to_not receive(:convert)
19
- expect(delegator.convert node).to eq described_class::NOT_FOUND_MARKER
20
- end
21
-
22
- it 'prints a warning on stderr' do
23
- expect { delegator.convert node }.to output(/Could not find a custom template/i).to_stderr
24
- end
25
- end
26
-
27
- context 'when template is found' do
28
- let(:handles) { true }
29
-
30
- it 'delegates to the original #convert and returns result' do
31
- expect(converter).to receive(:convert)
32
- .with(node, 'block_foo', {}).and_return('allons-y!')
33
-
34
- expect(delegator.convert node).to eq 'allons-y!'
35
- end
36
- end
37
- end
38
- end
@@ -1,289 +0,0 @@
1
- require 'fileutils'
2
- require 'forwardable'
3
-
4
- using Corefines::Array::second
5
-
6
- shared_examples DocTest::IO::Base do
7
-
8
- subject(:suite) { described_class.new(file_ext: '.adoc', path: path) }
9
- let(:path) { ['/tmp/alpha', '/tmp/beta'] }
10
-
11
-
12
- describe '#initialize' do
13
-
14
- subject(:init) { described_class.new(args) }
15
- let(:args) { {} }
16
-
17
- {'nil' => nil, 'blank' => ' '}.each do |desc, file_ext|
18
- context "with file_ext #{desc}" do
19
- let(:args) { {file_ext: file_ext, path: path} }
20
- it { expect { init }.to raise_error ArgumentError }
21
- end
22
- end
23
-
24
- context 'with path string' do
25
- let(:args) { {file_ext: '.html', path: '/foo/bar'} }
26
-
27
- it 'wraps string to array' do
28
- is_expected.to have_attributes(path: ['/foo/bar'])
29
- end
30
- end
31
- end
32
-
33
-
34
- describe '#parse' do
35
- context 'empty file' do
36
- subject { suite.parse '', 'block_ulist' }
37
-
38
- it { is_expected.to be_empty }
39
- end
40
- end
41
-
42
-
43
- describe '#read_examples' do
44
- include FakeFS::SpecHelpers
45
-
46
- subject(:result) { suite.read_examples group_name }
47
-
48
- let(:group_name) { 'section' }
49
-
50
- before do
51
- path.each { |p| FileUtils.mkpath p }
52
- create_and_write_group path.first, 'noise', '.adoc', 'foo', 'bar'
53
-
54
- allow(suite).to receive(:parse) do |input, group_name|
55
- path, file_name, *example_names = input.split("\n")
56
- expect(group_name).to eq file_name.split('.').first
57
-
58
- example_names.map do |name|
59
- # this content is just filling to identify the example in test
60
- create_example [group_name, name], content: "#{path}/#{file_name}:#{name}"
61
- end
62
- end
63
- end
64
-
65
- context "when the group's file is not found" do
66
- it { is_expected.to be_empty }
67
- end
68
-
69
- context "when the group's file has a wrong file extension" do
70
- before do
71
- create_and_write_group path.first, group_name, '.html', 'level1', 'level2'
72
- end
73
-
74
- it { is_expected.to be_empty }
75
- end
76
-
77
- context 'when single group file is found' do
78
- let! :examples do
79
- create_and_write_group path.second, group_name, '.adoc', 'level1', 'level2'
80
- end
81
-
82
- it 'returns parsed examples' do
83
- is_expected.to eq examples
84
- end
85
- end
86
-
87
- context 'when multiple group files are found and contains example with same name' do
88
- let! :examples do
89
- first = create_and_write_group path.first, group_name, '.adoc', 'level1', 'level2'
90
- second = create_and_write_group path.second, group_name, '.adoc', 'level2', 'level3'
91
- [*first, second[1]]
92
- end
93
-
94
- it 'returns parsed examples without duplicates (first wins)' do
95
- is_expected.to eq examples
96
- end
97
- end
98
- end
99
-
100
-
101
- describe '#write_examples' do
102
- include FakeFS::SpecHelpers
103
-
104
- let :examples do
105
- (1..2).map { |i| create_example "section:level#{i}", content: 'yada' }
106
- end
107
-
108
- before { path.each { |p| FileUtils.mkpath p } }
109
-
110
- it 'writes serialized examples to file named after the group with file extension' do
111
- expect(suite).to receive :serialize do |exmpls|
112
- exmpls.map(&:name).join("\n")
113
- end
114
- suite.write_examples examples
115
-
116
- file = File.read "#{path.first}/section.adoc"
117
- expect(file).to eq examples.map(&:name).join("\n")
118
- end
119
- end
120
-
121
-
122
- describe '#file_names' do
123
- include FakeFS::SpecHelpers
124
-
125
- subject(:result) { suite.group_names }
126
-
127
- before { path.each { |p| FileUtils.mkpath p } }
128
-
129
- context 'when no file is found' do
130
- it { is_expected.to be_empty }
131
- end
132
-
133
- it 'returns names of files with matching file extension only' do
134
- %w[block_image.html block_ulist.adoc].each do |name|
135
- File.write "#{path.first}/#{name}", 'yada'
136
- end
137
- is_expected.to contain_exactly 'block_ulist'
138
- end
139
-
140
- it 'returns names sorted and deduplicated' do
141
- (names = %w[z j d c k d]).each_with_index do |name, i|
142
- File.write "#{path[i % 2]}/#{name}.adoc", 'yada'
143
- end
144
-
145
- is_expected.to eq names.uniq.sort
146
- end
147
-
148
- it 'ignores directories and files in subdirectories' do
149
- Dir.mkdir "#{path.first}/invalid.adoc"
150
- File.write "#{path.first}/invalid.adoc/wat.adoc", 'yada'
151
-
152
- is_expected.to be_empty
153
- end
154
- end
155
-
156
-
157
- describe '#update_examples' do
158
-
159
- let :current do
160
- %w[gr0:ex0 gr0:ex1 gr1:ex0 gr1:ex1].map do |name|
161
- create_example name, content: name.reverse
162
- end
163
- end
164
- let :updated do
165
- [ (create_example 'gr0:ex0', content: 'allons-y!'),
166
- (create_example 'gr1:ex1', content: 'allons-y!') ]
167
- end
168
-
169
- before do
170
- expect(suite).to receive(:read_examples).exactly(2).times do |group_name|
171
- current.select { |e| e.group_name == group_name }
172
- end
173
- end
174
-
175
- it 'merges current and updated examples and writes them' do
176
- is_expected.to receive(:write_examples).with [updated[0], current[1]]
177
- is_expected.to receive(:write_examples).with [current[2], updated[1]]
178
-
179
- suite.update_examples updated
180
- end
181
- end
182
-
183
-
184
- describe '#pair_with' do
185
-
186
- subject(:result) { ours_suite.pair_with(theirs_suite).to_a }
187
- let(:result_names) { result.map(&:first).map(&:name) }
188
-
189
- let(:ours_suite) { described_class.new(file_ext: '.xyz') }
190
- let(:theirs_suite) { DocTest::IO::Asciidoc.new(file_ext: '.adoc') }
191
-
192
- def ours_exmpl(suffix, group = 0)
193
- create_example "gr#{group}:ex#{suffix}", content: 'ours!'
194
- end
195
-
196
- def theirs_exmpl(suffix, group = 0)
197
- create_example "gr#{group}:ex#{suffix}", content: 'theirs!'
198
- end
199
-
200
- before do
201
- expect(ours_suite).to receive(:group_names)
202
- .and_return(['gr0', 'gr1'])
203
- expect(theirs_suite).to receive(:read_examples)
204
- .with(/gr[0-1]/).exactly(:twice).and_return(*theirs)
205
- expect(ours_suite).to receive(:read_examples)
206
- .with(/gr[0-1]/).exactly(:twice).and_return(*ours)
207
- end
208
-
209
- context do
210
- let :ours do
211
- [ [ ours_exmpl(0, 0), ours_exmpl(1, 0) ], [ ours_exmpl(0, 1), ours_exmpl(1, 1) ] ]
212
- end
213
- let :theirs do
214
- [ [ theirs_exmpl(1, 0), theirs_exmpl(0, 0) ], [ theirs_exmpl(0, 1), theirs_exmpl(1, 1) ] ]
215
- end
216
-
217
- it 'returns pairs of ours/theirs examples in ours order' do
218
- expect(result_names).to eq %w[gr0:ex0 gr0:ex1 gr1:ex0 gr1:ex1]
219
- expect(result).to eq ours.flatten(1).zip theirs.flatten(1).sort_by(&:name)
220
- end
221
- end
222
-
223
- context 'when some example is missing' do
224
- let(:ours) { [(0..2).map { |i| ours_exmpl i }, []] }
225
- let(:theirs) { [[1, 0, 2].map { |i| theirs_exmpl i }, []] }
226
-
227
- context 'in theirs suite' do
228
- let(:theirs) { [ [theirs_exmpl(2), theirs_exmpl(0)], [] ] }
229
-
230
- it 'returns pairs in ours order' do
231
- expect(result_names).to eq %w[gr0:ex0 gr0:ex1 gr0:ex2]
232
- end
233
-
234
- it 'replaces the missing example with empty one with the name' do
235
- expect(result.second.last).to eq create_example 'gr0:ex1'
236
- end
237
- end
238
-
239
- context 'in ours suite' do
240
- let(:ours) { [ [ours_exmpl(1), ours_exmpl(2)], [] ] }
241
-
242
- it 'returns pairs in ours order with the missing example at the end' do
243
- expect(result_names).to eq %w[gr0:ex1 gr0:ex2 gr0:ex0]
244
- end
245
-
246
- it 'replaces the missing example with empty one with the name' do
247
- expect(result.last.first).to eq create_example 'gr0:ex0'
248
- end
249
- end
250
- end
251
- end
252
-
253
-
254
- describe '#format_options' do
255
-
256
- shared_examples :format_options do |input, output|
257
- it "returns #{output} for #{input}" do
258
- expect(suite.send(:format_options, input)).to eq output
259
- end
260
- end
261
-
262
- context 'empty' do
263
- include_examples :format_options, {}, []
264
- end
265
-
266
- context 'options with one value' do
267
- include_examples :format_options, {opt1: 'val1', opt2: 'val2'}, [':opt1: val1', ':opt2: val2']
268
- end
269
-
270
- context 'options with multiple values' do
271
- include_examples :format_options, {opt1: %w[val11 val12], opt2: ['val2']},
272
- [':opt1: val11', ':opt1: val12', ':opt2: val2']
273
- end
274
-
275
- context 'boolean options' do
276
- include_examples :format_options, {opt1: true, opt2: false}, [':opt1:', ':opt2: false']
277
- end
278
- end
279
-
280
-
281
- def create_and_write_group(path, group_name, file_ext, *examples)
282
- content = [path, group_name + file_ext, *examples].join("\n")
283
- File.write File.join(path, group_name + file_ext), content
284
-
285
- examples.map do |name|
286
- create_example [group_name, name], content: "#{path}/#{group_name}#{file_ext}:#{name}"
287
- end
288
- end
289
- end
data/spec/spec_helper.rb DELETED
@@ -1,39 +0,0 @@
1
- require 'corefines'
2
- require 'rspec/collection_matchers'
3
- require 'simplecov'
4
- require 'asciidoctor/doctest'
5
- require 'fakefs/spec_helpers'
6
-
7
- Dir['./spec/{shared_examples,support}/**/*.rb'].each { |file| require file }
8
-
9
- RSpec.configure do |config|
10
-
11
- # rspec-expectations config
12
- config.expect_with :rspec do |expects|
13
-
14
- # This option disables deprecated 'should' syntax.
15
- expects.syntax = :expect
16
-
17
- # This option makes the +description+ and +failure_message+ of custom
18
- # matchers include text for helper methods defined using +chain+, e.g.:
19
- # be_bigger_than(2).and_smaller_than(4).description
20
- # # => "be bigger than 2 and smaller than 4"
21
- # ...rather than:
22
- # # => "be bigger than 2"
23
- expects.include_chain_clauses_in_custom_matcher_descriptions = true
24
- end
25
-
26
- # rspec-mocks config
27
- config.mock_with :rspec do |mocks|
28
-
29
- # Prevents you from mocking or stubbing a method that does not exist on
30
- # a real object.
31
- mocks.verify_partial_doubles = true
32
- end
33
-
34
- config.color = true
35
- end
36
-
37
- def create_example(*args)
38
- DocTest::Example.new(*args)
39
- end
@@ -1,7 +0,0 @@
1
- require 'rspec/expectations'
2
-
3
- RSpec::Matchers.define :have_method do |*names|
4
- match do |klass|
5
- names.all? { |name| klass.method_defined? name }
6
- end
7
- end
data/spec/tester_spec.rb DELETED
@@ -1,153 +0,0 @@
1
- describe DocTest::Tester do
2
-
3
- subject(:tester) { described_class.new(input_suite, output_suite, converter, reporter) }
4
-
5
- let(:converter) { double 'converter' }
6
- let(:input_suite) { double 'ExamplesSuite' }
7
- let(:output_suite) { double 'ExamplesSuite' }
8
- let(:reporter) { spy 'Reporter' }
9
- let(:examples) { fixtures }
10
-
11
-
12
- describe '#initialize' do
13
-
14
- context "with default reporter" do
15
- subject(:tester) { described_class.new(input_suite, output_suite, converter, nil) }
16
-
17
- it { expect(tester.reporter).to be_a DocTest::TestReporter }
18
- end
19
- end
20
-
21
-
22
- describe '#run_tests' do
23
-
24
- before do |ex|
25
- next if ex.metadata[:skip_before]
26
-
27
- expect(input_suite).to receive(:pair_with)
28
- .with(output_suite).and_return(examples.values)
29
- allow(tester).to receive(:test_example)
30
- end
31
-
32
- context "with default pattern" do
33
-
34
- it "pairs the examples and tests all valid pairs" do
35
- ['ex:alpha', 'ex:beta', 'ex:nooutput'].each do |name|
36
- expect(tester).to receive(:test_example).with(*examples[name])
37
- end
38
- tester.run_tests
39
- end
40
- end
41
-
42
- context "with specific pattern" do
43
-
44
- it "pairs the examples and tests those matching the pattern" do
45
- expect(tester).to receive(:test_example).with(*examples['ex:beta'])
46
- tester.run_tests(pattern: 'ex:b*')
47
- end
48
- end
49
-
50
- it "ignores pairs with empty input example" do
51
- expect(tester).to_not receive(:test_example).with(*examples['ex:noinput'])
52
- tester.run_tests
53
- end
54
-
55
- it "calls reporter's methods in the correct order", :skip_before do
56
- expect(reporter).to receive(:start).ordered
57
- expect(input_suite).to receive(:pair_with).and_return([]).ordered
58
- expect(reporter).to receive(:report).ordered
59
- expect(reporter).to receive(:passed?).ordered
60
-
61
- tester.run_tests
62
- end
63
- end
64
-
65
-
66
- describe '#test_example' do
67
-
68
- subject(:failures) { tester.test_example input_exmpl, output_exmpl }
69
-
70
- let(:examples_pair) { examples['ex:alpha'] }
71
- let(:input_exmpl) { examples_pair[0] }
72
- let(:output_exmpl) { examples_pair[1] }
73
- let(:actual) { output_exmpl.content }
74
- let(:expected) { output_exmpl.content }
75
-
76
- shared_examples :example do
77
- it "calls reporter" do
78
- expect(reporter).to receive(:record)
79
- tester.test_example input_exmpl, output_exmpl
80
- end
81
- end
82
-
83
- before do |ex|
84
- next if ex.metadata[:skip_before]
85
-
86
- expect(converter).to receive(:convert_examples)
87
- .with(input_exmpl, output_exmpl)
88
- .and_return([actual, expected])
89
- end
90
-
91
-
92
- context "when output example is empty", :skip_before do
93
-
94
- let(:examples_pair) { examples['ex:nooutput'] }
95
-
96
- it "skips the test" do
97
- is_expected.to contain_exactly Minitest::Skip
98
- end
99
-
100
- include_examples :example
101
- end
102
-
103
- context "when examples are equivalent" do
104
-
105
- it "returns no failure" do
106
- is_expected.to be_empty
107
- end
108
-
109
- include_examples :example
110
- end
111
-
112
- context "when examples are not equivalent" do
113
-
114
- let(:actual) { '<em>meh</em>' }
115
-
116
- it "returns failure" do
117
- is_expected.to include Minitest::Assertion
118
- end
119
-
120
- context "and input example has desc:" do
121
-
122
- it "returns failure with message that starts with the desc" do
123
- expect(failures.first.message).to match /^yada.*/
124
- end
125
- end
126
-
127
- context "and both input and output examples have desc:" do
128
-
129
- let(:examples_pair) { examples['ex:beta'] }
130
-
131
- it "returns failure with message that starts with the output's example desc" do
132
- expect(failures.first.message).to match /^Yoda.*/
133
- end
134
- end
135
-
136
- include_examples :example
137
- end
138
- end
139
-
140
-
141
- def fixtures
142
- data = {
143
- 'ex:alpha' => [ {content: '_alpha_', desc: 'yada'}, {content: '<i>alpha</i>'} ],
144
- 'ex:beta' => [ {content: '*beta*', desc: 'yada'}, {content: '<b>beta</b>', desc: 'Yoda'} ],
145
- 'ex:noinput' => [ {}, {content: '<del>noinput</del>'} ],
146
- 'ex:nooutput' => [ {content: 'nooutput'}, {} ]
147
- }
148
- data = data.map { |name, tuple|
149
- [ name, tuple.map { |opts| DocTest::Example.new(name, opts) } ]
150
- }
151
- Hash[data]
152
- end
153
- end