test/extensions_test.rb in asciidoctor-1.5.5 vs test/extensions_test.rb in asciidoctor-1.5.6
- old
+ new
@@ -1,12 +1,29 @@
# encoding: UTF-8
unless defined? ASCIIDOCTOR_PROJECT_DIR
$: << File.dirname(__FILE__); $:.uniq!
require 'test_helper'
end
-require 'asciidoctor/extensions'
+class ExtensionsInitTest < Minitest::Test
+ def test_autoload
+ doc = empty_document
+ refute doc.extensions?, 'Extensions should not be enabled by default'
+
+ begin
+ # NOTE trigger extensions to autoload
+ Asciidoctor::Extensions.groups
+ rescue; end
+
+ doc = empty_document
+ assert doc.extensions?, 'Extensions should be enabled after being autoloaded'
+
+ self.class.remove_tests self.class
+ end
+ self
+end.new(nil).test_autoload
+
class SamplePreprocessor < Asciidoctor::Extensions::Preprocessor
def process doc, reader
nil
end
end
@@ -15,12 +32,17 @@
end
class SampleDocinfoProcessor < Asciidoctor::Extensions::DocinfoProcessor
end
+# NOTE intentionally using the deprecated name
class SampleTreeprocessor < Asciidoctor::Extensions::Treeprocessor
+ def process document
+ nil
+ end
end
+SampleTreeProcessor = SampleTreeprocessor
class SamplePostprocessor < Asciidoctor::Extensions::Postprocessor
end
class SampleBlock < Asciidoctor::Extensions::BlockProcessor
@@ -59,19 +81,19 @@
nil
end
end
end
-class ReplaceAuthorTreeprocessor < Asciidoctor::Extensions::Treeprocessor
+class ReplaceAuthorTreeProcessor < Asciidoctor::Extensions::TreeProcessor
def process document
document.attributes['firstname'] = 'Ghost'
document.attributes['author'] = 'Ghost Writer'
document
end
end
-class ReplaceTreeTreeprocessor < Asciidoctor::Extensions::Treeprocessor
+class ReplaceTreeTreeProcessor < Asciidoctor::Extensions::TreeProcessor
def process document
if document.doctitle == 'Original Document'
Asciidoctor.load %(== Replacement Document\nReplacement Author\n\ncontent)
else
document
@@ -84,49 +106,68 @@
output.gsub(/<(\w+).*?>/m, "<\\1>")
end
end
class UppercaseBlock < Asciidoctor::Extensions::BlockProcessor; use_dsl
- match_name :yell
- on_contexts :paragraph
- parse_content_as :simple
+ named :yell
+ bound_to :paragraph
+ parses_content_as :simple
def process parent, reader, attributes
create_paragraph parent, reader.lines.map(&:upcase), attributes
end
end
class SnippetMacro < Asciidoctor::Extensions::BlockMacroProcessor
def process parent, target, attributes
- create_pass_block parent, %(<script src="http://example.com/#{target}.js"></script>), {}, :content_model => :raw
+ create_pass_block parent, %(<script src="http://example.com/#{target}.js?_mode=#{attributes['mode']}"></script>), {}, :content_model => :raw
end
end
class TemperatureMacro < Asciidoctor::Extensions::InlineMacroProcessor; use_dsl
named :degrees
- name_attributes 'units'
+ resolves_attributes '1:units', 'precision=1'
def process parent, target, attributes
units = attributes['units'] || (parent.document.attr 'temperature-unit', 'C')
+ precision = attributes['precision'].to_i
c = target.to_f
case units
when 'C'
- %(#{c} °C)
+ %(#{round_with_precision c, precision} °C)
when 'F'
- %(#{c * 1.8 + 32 } °F)
+ %(#{round_with_precision c * 1.8 + 32, precision} °F)
else
- c
+ raise ::ArgumentError, %(Unknown temperature units: #{units})
end
end
+
+ if (::Numeric.instance_method :round).arity == 0
+ def round_with_precision value, precision = 0
+ if precision == 0
+ value.round
+ else
+ factor = 10 ** precision
+ if precision < 0
+ (value * factor).round.div factor
+ else
+ (value * factor).round.fdiv factor
+ end
+ end
+ end
+ else
+ def round_with_precision value, precision = 0
+ value.round precision
+ end
+ end
end
class MetaRobotsDocinfoProcessor < Asciidoctor::Extensions::DocinfoProcessor
def process document
'<meta name="robots" content="index,follow">'
end
end
-class MetaAppDocinfoProcessor < Asciidoctor::Extensions::DocinfoProcessor
- use_dsl
+class MetaAppDocinfoProcessor < Asciidoctor::Extensions::DocinfoProcessor; use_dsl
at_location :head
def process document
'<meta name="application-name" content="Asciidoctor App">'
end
@@ -195,48 +236,113 @@
ensure
Asciidoctor::Extensions.unregister_all
end
end
+ test 'should unregister extension group by symbol name' do
+ begin
+ Asciidoctor::Extensions.register :sample, SampleExtensionGroup
+ refute_nil Asciidoctor::Extensions.groups
+ assert_equal 1, Asciidoctor::Extensions.groups.size
+ Asciidoctor::Extensions.unregister :sample
+ assert_equal 0, Asciidoctor::Extensions.groups.size
+ ensure
+ Asciidoctor::Extensions.unregister_all
+ end
+ end
+
+ test 'should unregister extension group by string name' do
+ begin
+ Asciidoctor::Extensions.register :sample, SampleExtensionGroup
+ refute_nil Asciidoctor::Extensions.groups
+ assert_equal 1, Asciidoctor::Extensions.groups.size
+ Asciidoctor::Extensions.unregister 'sample'
+ assert_equal 0, Asciidoctor::Extensions.groups.size
+ ensure
+ Asciidoctor::Extensions.unregister_all
+ end
+ end
+
+ test 'should unregister multiple extension groups by name' do
+ begin
+ Asciidoctor::Extensions.register :sample1, SampleExtensionGroup
+ Asciidoctor::Extensions.register :sample2, SampleExtensionGroup
+ refute_nil Asciidoctor::Extensions.groups
+ assert_equal 2, Asciidoctor::Extensions.groups.size
+ Asciidoctor::Extensions.unregister :sample1, :sample2
+ assert_equal 0, Asciidoctor::Extensions.groups.size
+ ensure
+ Asciidoctor::Extensions.unregister_all
+ end
+ end
+
test 'should get class for top-level class name' do
- clazz = Asciidoctor::Extensions.class_for_name('Asciidoctor')
+ clazz = Asciidoctor::Extensions.class_for_name 'String'
refute_nil clazz
- assert_equal Asciidoctor, clazz
+ assert_equal String, clazz
end
test 'should get class for class name in module' do
- clazz = Asciidoctor::Extensions.class_for_name('Asciidoctor::Extensions')
+ clazz = Asciidoctor::Extensions.class_for_name 'Asciidoctor::Document'
refute_nil clazz
- assert_equal Asciidoctor::Extensions, clazz
+ assert_equal Asciidoctor::Document, clazz
end
test 'should get class for class name resolved from root' do
- clazz = Asciidoctor::Extensions.class_for_name('::Asciidoctor::Extensions')
+ clazz = Asciidoctor::Extensions.class_for_name '::Asciidoctor::Document'
refute_nil clazz
- assert_equal Asciidoctor::Extensions, clazz
+ assert_equal Asciidoctor::Document, clazz
end
test 'should raise exception if cannot find class for name' do
begin
- Asciidoctor::Extensions.class_for_name('InvalidModule::InvalidClass')
+ Asciidoctor::Extensions.class_for_name 'InvalidModule::InvalidClass'
flunk 'Expecting RuntimeError to be raised'
- rescue RuntimeError => e
+ rescue NameError => e
assert_equal 'Could not resolve class for name: InvalidModule::InvalidClass', e.message
end
end
+ test 'should raise exception if name resolves to module' do
+ begin
+ Asciidoctor::Extensions.class_for_name 'Asciidoctor::Extensions'
+ flunk 'Expecting RuntimeError to be raised'
+ rescue NameError => e
+ assert_equal 'Could not resolve class for name: Asciidoctor::Extensions', e.message
+ end
+ end
+
test 'should resolve class if class is given' do
- clazz = Asciidoctor::Extensions.resolve_class(Asciidoctor::Extensions)
+ clazz = Asciidoctor::Extensions.resolve_class Asciidoctor::Document
refute_nil clazz
- assert_equal Asciidoctor::Extensions, clazz
+ assert_equal Asciidoctor::Document, clazz
end
test 'should resolve class if class from string' do
- clazz = Asciidoctor::Extensions.resolve_class('Asciidoctor::Extensions')
+ clazz = Asciidoctor::Extensions.resolve_class 'Asciidoctor::Document'
refute_nil clazz
- assert_equal Asciidoctor::Extensions, clazz
+ assert_equal Asciidoctor::Document, clazz
end
+
+ test 'should allow standalone registry to be created but not registered' do
+ registry = Asciidoctor::Extensions.create 'sample' do
+ block do
+ named :whisper
+ bound_to :paragraph
+ parses_content_as :simple
+ def process parent, reader, attributes
+ create_paragraph parent, reader.lines.map(&:downcase), attributes
+ end
+ end
+ end
+
+ assert_instance_of Asciidoctor::Extensions::Registry, registry
+ refute_nil registry.groups
+ assert_equal 1, registry.groups.size
+ assert_equal 'sample', registry.groups.keys.first
+ assert_equal 0, Asciidoctor::Extensions.groups.size
+ end
end
context 'Activate' do
test 'should call activate on extension group class' do
begin
@@ -316,11 +422,12 @@
assert extensions.first.is_a? Asciidoctor::Extensions::ProcessorExtension
assert extensions.first.instance.is_a? SampleDocinfoProcessor
assert extensions.first.process_method.is_a? ::Method
end
- test 'should instantiate treeprocessors' do
+ # NOTE intentionally using the legacy names
+ test 'should instantiate tree processors' do
registry = Asciidoctor::Extensions::Registry.new
registry.treeprocessor SampleTreeprocessor
registry.activate Asciidoctor::Document.new
assert registry.treeprocessors?
extensions = registry.treeprocessors
@@ -395,10 +502,34 @@
assert extensions.first.is_a? Asciidoctor::Extensions::ProcessorExtension
end
end
context 'Integration' do
+ test 'can provide extension registry as option' do
+ registry = Asciidoctor::Extensions.create do
+ tree_processor SampleTreeProcessor
+ end
+
+ doc = document_from_string %(= Document Title\n\ncontent), :extension_registry => registry
+ refute_nil doc.extensions
+ assert_equal 1, doc.extensions.groups.size
+ assert doc.extensions.tree_processors?
+ assert_equal 1, doc.extensions.tree_processors.size
+ assert_equal 0, Asciidoctor::Extensions.groups.size
+ end
+
+ test 'can provide extensions proc as option' do
+ doc = document_from_string %(= Document Title\n\ncontent), :extensions => proc {
+ tree_processor SampleTreeProcessor
+ }
+ refute_nil doc.extensions
+ assert_equal 1, doc.extensions.groups.size
+ assert doc.extensions.tree_processors?
+ assert_equal 1, doc.extensions.tree_processors.size
+ assert_equal 0, Asciidoctor::Extensions.groups.size
+ end
+
test 'should invoke preprocessors before parsing document' do
input = <<-EOS
junk line
= Document Title
@@ -455,71 +586,106 @@
EOS
# Safe Mode is not required here
document = empty_document :base_dir => File.expand_path(File.dirname(__FILE__))
document.extensions.include_processor do
+ handles? do |target|
+ target == 'include-file.asciidoc'
+ end
+
process do |doc, reader, target, attributes|
# demonstrate that push_include normalizes endlines
content = ["include target:: #{target}\n", "\n", "middle line\n"]
reader.push_include content, target, target, 1, attributes
end
end
- reader = Asciidoctor::PreprocessorReader.new document, input
+ reader = Asciidoctor::PreprocessorReader.new document, input, nil, :normalize => true
lines = []
lines << reader.read_line
lines << reader.read_line
lines << reader.read_line
assert_equal 'include target:: include-file.asciidoc', lines.last
assert_equal 'include-file.asciidoc: line 2', reader.line_info
while reader.has_more_lines?
lines << reader.read_line
end
- source = lines * ::Asciidoctor::EOL
+ source = lines * ::Asciidoctor::LF
assert_match(/^include target:: include-file.asciidoc$/, source)
assert_match(/^middle line$/, source)
end
- test 'should invoke treeprocessors after parsing document' do
+ test 'should invoke tree processors after parsing document' do
input = <<-EOS
= Document Title
Doc Writer
content
EOS
begin
Asciidoctor::Extensions.register do
- treeprocessor ReplaceAuthorTreeprocessor
+ tree_processor ReplaceAuthorTreeProcessor
end
doc = document_from_string input
assert_equal 'Ghost Writer', doc.author
ensure
Asciidoctor::Extensions.unregister_all
end
end
- test 'should allow treeprocessor to replace tree' do
+ test 'should allow tree processor to replace tree' do
input = <<-EOS
= Original Document
Doc Writer
content
EOS
begin
Asciidoctor::Extensions.register do
- treeprocessor ReplaceTreeTreeprocessor
+ tree_processor ReplaceTreeTreeProcessor
end
doc = document_from_string input
assert_equal 'Replacement Document', doc.doctitle
ensure
Asciidoctor::Extensions.unregister_all
end
end
+ test 'should honor block title assigned in tree processor' do
+ input = <<-EOS
+= Document Title
+:!example-caption:
+
+.Old block title
+====
+example block content
+====
+ EOS
+
+ old_title = nil
+ begin
+ Asciidoctor::Extensions.register do
+ tree_processor do
+ process do |doc|
+ ex = (doc.find_by :context => :example)[0]
+ old_title = ex.title
+ ex.title = 'New block title'
+ end
+ end
+ end
+
+ doc = document_from_string input
+ assert_equal 'Old block title', old_title
+ assert_equal 'New block title', (doc.find_by :context => :example)[0].title
+ ensure
+ Asciidoctor::Extensions.unregister_all
+ end
+ end
+
test 'should invoke postprocessors after rendering document' do
input = <<-EOS
* one
* two
* three
@@ -554,49 +720,103 @@
ensure
Asciidoctor::Extensions.unregister_all
end
end
+ test 'should pass cloaked context in attributes passed to process method of custom block' do
+ input = <<-EOS
+[custom]
+****
+sidebar
+****
+ EOS
+
+ cloaked_context = nil
+ begin
+ Asciidoctor::Extensions.register do
+ block :custom do
+ on_context :sidebar
+ process do |doc, reader, attrs|
+ cloaked_context = attrs['cloaked-context']
+ nil
+ end
+ end
+ end
+
+ render_embedded_string input
+ assert_equal :sidebar, cloaked_context
+ ensure
+ Asciidoctor::Extensions.unregister_all
+ end
+ end
+
test 'should invoke processor for custom block macro' do
input = <<-EOS
-snippet::12345[]
+snippet::12345[mode=edit]
EOS
begin
Asciidoctor::Extensions.register do
block_macro SnippetMacro, :snippet
end
output = render_embedded_string input
- assert output.include?('<script src="http://example.com/12345.js"></script>')
+ assert output.include?('<script src="http://example.com/12345.js?_mode=edit"></script>')
ensure
Asciidoctor::Extensions.unregister_all
end
end
+ test 'should match short form of block macro' do
+ input = <<-EOS
+custom_toc::[]
+ EOS
+
+ resolved_target = nil
+
+ begin
+ Asciidoctor::Extensions.register do
+ block_macro do
+ named :custom_toc
+ process do |parent, target, attrs|
+ resolved_target = target
+ create_pass_block parent, '<!-- custom toc goes here -->', {}, :content_model => :raw
+ end
+ end
+ end
+
+ output = render_embedded_string input
+ assert_equal '<!-- custom toc goes here -->', output
+ assert_equal '', resolved_target
+ ensure
+ Asciidoctor::Extensions.unregister_all
+ end
+ end
+
test 'should invoke processor for custom inline macro' do
begin
Asciidoctor::Extensions.register do
- inline_macro TemperatureMacro, :degrees
+ inline_macro TemperatureMacro, :deg
end
- output = render_embedded_string 'Room temperature is degrees:25[C].', :attributes => {'temperature-unit' => 'F'}
- assert output.include?('Room temperature is 25.0 °C.')
+ output = render_embedded_string 'Room temperature is deg:25[C,precision=0].', :attributes => { 'temperature-unit' => 'F' }
+ assert output.include?('Room temperature is 25 °C.')
- output = render_embedded_string 'Room temperature is degrees:25[].', :attributes => {'temperature-unit' => 'F'}
- assert output.include?('Room temperature is 77.0 °F.')
+ output = render_embedded_string 'Normal body temperature is deg:37[].', :attributes => { 'temperature-unit' => 'F' }
+ assert output.include?('Normal body temperature is 98.6 °F.')
ensure
Asciidoctor::Extensions.unregister_all
end
end
test 'should resolve regexp for inline macro lazily' do
begin
Asciidoctor::Extensions.register do
inline_macro do
named :label
- using_format :short
+ with_format :short
+ resolves_attributes false
process do |parent, target|
%(<label>#{target}</label>)
end
end
end
@@ -606,17 +826,118 @@
ensure
Asciidoctor::Extensions.unregister_all
end
end
+ test 'should assign captures correctly for inline macros' do
+ begin
+ Asciidoctor::Extensions.register do
+ inline_macro do
+ named :short_attributes
+ with_format :short
+ resolves_attributes '1:name'
+ process do |parent, target, attrs|
+ %(target=#{target.inspect}, attributes=#{attrs.sort_by {|k, _| k.to_s }.inspect})
+ end
+ end
+
+ inline_macro do
+ named :short_text
+ with_format :short
+ resolves_attributes false
+ process do |parent, target, attrs|
+ %(target=#{target.inspect}, attributes=#{attrs.sort_by {|k, _| k.to_s }.inspect})
+ end
+ end
+
+ inline_macro do
+ named :full_attributes
+ resolves_attributes '1:name' => nil
+ process do |parent, target, attrs|
+ %(target=#{target.inspect}, attributes=#{attrs.sort_by {|k, _| k.to_s }.inspect})
+ end
+ end
+
+ inline_macro do
+ named :full_text
+ resolves_attributes false
+ process do |parent, target, attrs|
+ %(target=#{target.inspect}, attributes=#{attrs.sort_by {|k, _| k.to_s }.inspect})
+ end
+ end
+
+ inline_macro do
+ named :@short_match
+ matching %r/@(\w+)/
+ resolves_attributes false
+ process do |parent, target, attrs|
+ %(target=#{target.inspect}, attributes=#{attrs.sort_by {|k, _| k.to_s }.inspect})
+ end
+ end
+ end
+
+ input = <<-EOS
+[subs=normal]
+++++
+short_attributes:[]
+short_attributes:[value,key=val]
+short_text:[]
+short_text:[[text\\]]
+full_attributes:target[]
+full_attributes:target[value,key=val]
+full_text:target[]
+full_text:target[[text\\]]
+@target
+++++
+ EOS
+ expected = <<-EOS.chomp
+target="", attributes=[]
+target="value,key=val", attributes=[[1, "value"], ["key", "val"], ["name", "value"]]
+target="", attributes=[["text", ""]]
+target="[text]", attributes=[["text", "[text]"]]
+target="target", attributes=[]
+target="target", attributes=[[1, "value"], ["key", "val"], ["name", "value"]]
+target="target", attributes=[["text", ""]]
+target="target", attributes=[["text", "[text]"]]
+target="target", attributes=[]
+ EOS
+ output = render_embedded_string input
+ assert_equal expected, output
+ ensure
+ Asciidoctor::Extensions.unregister_all
+ end
+ end
+
+ test 'should invoke convert on return value if value is an inline node' do
+ begin
+ Asciidoctor::Extensions.register do
+ inline_macro do
+ named :mention
+ resolves_attributes false
+ process do |parent, target, attrs|
+ if (text = attrs['text']).empty?
+ text = %(@#{target})
+ end
+ create_anchor parent, text, :type => :link, :target => %(https://github.com/#{target})
+ end
+ end
+ end
+
+ output = render_embedded_string 'mention:mojavelinux[Dan]'
+ assert output.include?('<a href="https://github.com/mojavelinux">Dan</a>')
+ ensure
+ Asciidoctor::Extensions.unregister_all
+ end
+ end
+
test 'should not carry over attributes if block processor returns nil' do
begin
Asciidoctor::Extensions.register do
block do
named :skip
on_context :paragraph
- parse_content_as :raw
+ parses_content_as :raw
process do |parent, reader, attrs|
nil
end
end
end
@@ -641,11 +962,11 @@
begin
Asciidoctor::Extensions.register do
block do
named :foo
on_context :paragraph
- parse_content_as :raw
+ parses_content_as :raw
process do |parent, reader, attrs|
original_attrs = attrs.dup
attrs.delete('title')
create_paragraph parent, reader.read_lines, original_attrs.merge('id' => 'value')
end
@@ -696,9 +1017,55 @@
wrap = doc.blocks[0]
assert_equal 2, wrap.blocks.size
assert_equal 2, wrap.blocks[0].attributes.size
assert_equal 2, wrap.blocks[1].attributes.size
assert_nil wrap.blocks[1].attributes['foo']
+ ensure
+ Asciidoctor::Extensions.unregister_all
+ end
+ end
+
+ test 'create_section should set up all section properties' do
+ begin
+ sect = nil
+ Asciidoctor::Extensions.register do
+ block_macro do
+ named :sect
+ process do |parent, target, attrs|
+ opts = (level = attrs.delete 'level') ? { :level => level.to_i } : {}
+ attrs['id'] = false if attrs['id'] == 'false'
+ sect = create_section parent, 'Section Title', attrs, opts
+ nil
+ end
+ end
+ end
+
+ input_tpl = <<-EOS
+= Document Title
+:doctype: book
+:sectnums:
+
+sect::[%s]
+ EOS
+
+ {
+ '' => ['chapter', 1, false, true, '_section_title'],
+ 'level=0' => ['part', 0, false, false, '_section_title'],
+ 'level=0,style=appendix' => ['appendix', 1, true, true, '_section_title'],
+ 'style=appendix' => ['appendix', 1, true, true, '_section_title'],
+ 'style=glossary' => ['glossary', 1, true, false, '_section_title'],
+ 'style=abstract' => ['chapter', 1, false, true, '_section_title'],
+ 'id=section-title' => ['chapter', 1, false, true, 'section-title'],
+ 'id=false' => ['chapter', 1, false, true, nil]
+ }.each do |attrlist, (expect_sectname, expect_level, expect_special, expect_numbered, expect_id)|
+ input = input_tpl % attrlist
+ document_from_string input, :safe => :server
+ assert_equal expect_sectname, sect.sectname
+ assert_equal expect_level, sect.level
+ assert_equal expect_special, sect.special
+ assert_equal expect_numbered, sect.numbered
+ assert_equal expect_id, sect.id
+ end
ensure
Asciidoctor::Extensions.unregister_all
end
end