resources/asciidoctor/spec/copy_images_spec.rb (349 lines of code) (raw):
# frozen_string_literal: true
require 'care_admonition/extension'
require 'change_admonition/extension'
require 'copy_images/extension'
require 'fileutils'
require 'tmpdir'
RSpec.describe CopyImages do
RSpec::Matchers.define_negated_matcher :not_match, :match
before(:each) do
Asciidoctor::Extensions.register CareAdmonition
Asciidoctor::Extensions.register ChangeAdmonition
Asciidoctor::Extensions.register CopyImages
end
after(:each) do
Asciidoctor::Extensions.unregister_all
end
include_context 'convert with logs'
# [] is the initial value but it is mutated by the conversion
let(:copied_storage) { [] }
let(:convert_attributes) do
{
'copy_image' => proc { |uri, source| copied_storage << [uri, source] },
}.tap do |attrs|
attrs['resources'] = resources if defined?(resources)
end
end
let(:copied) do
# Force evaluation of converted because it populates copied_storage
converted
copied_storage
end
# Absolute paths
let(:spec_dir) { File.dirname(__FILE__) }
let(:resources_dir) { "#{spec_dir}/resources/copy_images" }
# Full relative path to example images
let(:example1) { 'resources/copy_images/example1.png' }
let(:example2) { 'resources/copy_images/example2.png' }
##
# Asserts that a particular `image_command` copies the appropriate image
# when the image is referred to in many ways. The `image_command` should
# read `target` for the location of the image.
shared_examples 'copies images with various paths' do
let(:input) do
<<~ASCIIDOC
== Example
#{image_command}
ASCIIDOC
end
let(:include_line) { 2 }
let(:log_line) { include_line + log_offset }
##
# Asserts that some `input` causes just the `example1.png` image to
# be copied.
shared_examples 'copies example1' do
it 'copies the image' do
expect(copied).to eq([[resolved, "#{spec_dir}/#{example1}"]])
end
it 'logs that it copied the image' do
expect(logs).to include(
"INFO: <stdin>: line #{log_line}: copying #{spec_dir}/#{example1}"
)
end
end
shared_examples "when it can't find a file" do
let(:target) { 'not_found.jpg' }
it 'logs a warning' do
expect(logs).to match(expected_logs).and(not_match(/INFO: <stdin>/))
end
it "doesn't copy anything" do
expect(copied).to eq([])
end
end
context 'when the image ref matches that path exactly' do
let(:target) { example1 }
let(:resolved) { example1 }
include_examples 'copies example1'
end
context 'when the image ref is just the name of the image' do
let(:target) { 'example1.png' }
let(:resolved) { 'example1.png' }
include_examples 'copies example1'
end
context 'when the image ref matches the end of the path' do
let(:target) { 'copy_images/example1.png' }
let(:resolved) { 'copy_images/example1.png' }
include_examples 'copies example1'
end
context 'when the image contains attributes' do
let(:target) { 'example1.{ext}' }
let(:resolved) { 'example1.png' }
context 'when the attribute is close to the image' do
let(:input) do
<<~ASCIIDOC
== Example
:ext: png
#{image_command}
ASCIIDOC
end
let(:include_line) { 4 }
include_examples 'copies example1'
end
context 'when the attribute is far from the image' do
let(:input) do
<<~ASCIIDOC
== Example
:ext: png
Words.
#{image_command}
ASCIIDOC
end
let(:include_line) { 6 }
include_examples 'copies example1'
end
end
context 'when using the imagesdir attribute' do
let(:target) { 'example1.png' }
let(:resolved) { 'resources/copy_images/example1.png' }
let(:input) do
<<~ASCIIDOC
== Example
:imagesdir: resources/copy_images
#{image_command}
ASCIIDOC
end
let(:include_line) { 4 }
include_examples 'copies example1'
end
context 'when referencing an external image' do
let(:target) do
'https://f.cloud.github.com/assets/4320215/768165/19d8b1aa-e899-11e2-91bc-6b0553e8d722.png'
end
it "doesn't log anything" do
expect(logs).to eq('')
end
it "doesn't copy anything" do
expect(copied).to eq([])
end
end
context "when it can't find a file" do
include_examples "when it can't find a file"
let(:expected_logs) do
%r{WARN:\ <stdin>:\ line\ \d+:\ can't\ read\ image
\ \[not_found\.jpg\]\ at\ any\ of\ \[
"#{spec_dir}/not_found.jpg",\s
"#{spec_dir}/resources/not_found.jpg",\s
.+
"#{spec_dir}/resources/copy_images/not_found.jpg"
.+
\]}x
# Comment to fix syntax highlighting bug in VSCode....'
end
end
context 'when the resources attribute is invalid CSV' do
# Note that we still copy the images even with the invalid resources
include_examples 'copies example1'
let(:resources) { '"' }
let(:target) { 'example1.png' }
let(:resolved) { 'example1.png' }
it 'logs an error' do
expect(logs).to include(
"ERROR: <stdin>: line #{log_line}: Error loading [resources]: " \
'Unclosed quoted field on line 1.'
)
end
end
##
# Context and examples for testing copying from the directories in the
# `resources` attribute.
#
# Input:
# resources - set it to a comma separated list of directories
# containing #{tmp}
shared_examples 'copy with resources' do
let(:tmp) { Dir.mktmpdir }
before(:example) do
FileUtils.cp(
File.join(spec_dir, 'resources', 'copy_images', 'example1.png'),
File.join(tmp, 'tmp_example1.png')
)
end
after(:example) { FileUtils.remove_entry tmp }
context 'when the referenced image is in the resource directory' do
let(:target) { 'tmp_example1.png' }
it 'copies the image' do
expect(copied).to eq([[target, "#{tmp}/#{target}"]])
end
it 'logs that it copied the image' do
expect(logs).to eq(
"INFO: <stdin>: line #{log_line}: copying #{tmp}/#{target}"
)
end
end
context 'when the referenced image is in the doc directory' do
include_examples 'copies example1'
let(:target) { 'example1.png' }
let(:resolved) { 'example1.png' }
end
end
context 'when the resources attribute contains a single directory' do
let(:resources) { tmp }
include_examples 'copy with resources'
context "when it can't find a file" do
include_examples "when it can't find a file"
let(:expected_logs) do
%r{WARN:\ <stdin>:\ line\ \d+:\ can't\ read\ image
\ \[not_found\.jpg\]\ at\ any\ of\ \[
"#{tmp}/not_found.jpg",\s
"#{spec_dir}/not_found.jpg",\s
.+
\]}x
# Comment to fix syntax highlighting bug in VSCode....'
end
end
end
context 'when the resources attribute contains a multiple directories' do
let(:resources) { "/dummy1,#{tmp},/dummy2" }
include_examples 'copy with resources'
context "when it can't find a file" do
include_examples "when it can't find a file"
let(:expected_logs) do
%r{WARN:\ <stdin>:\ line\ \d+:\ can't\ read\ image
\ \[not_found\.jpg\] \ at\ any\ of\ \[
"/dummy1/not_found.jpg",\s
"/dummy2/not_found.jpg",\s
"#{tmp}/not_found.jpg",\s
"#{spec_dir}/not_found.jpg",\s
.+
\]}x
# Comment to fix syntax highlighting bug in VSCode....'
end
end
end
context 'when the resources attribute is empty' do
let(:resources) { '' }
let(:target) { example1 }
let(:resolved) { example1 }
include_examples 'copies example1'
end
end
let(:log_offset) { 0 }
context 'for the image block macro' do
let(:image_command) { "image::#{target}[]" }
include_examples 'copies images with various paths'
end
context 'for the image inline macro' do
let(:image_command) { "Words image:#{target}[] words" }
include_examples 'copies images with various paths'
context 'when the macro is escaped' do
let(:target) { 'example1.jpg' }
let(:input) do
<<~ASCIIDOC
== Example
"Words \\image:#{target}[] words"
ASCIIDOC
end
it "doesn't log anything" do
expect(logs).to eq('')
end
it "doesn't copy the image" do
expect(copied).to eq([])
end
end
context 'when there are multiple images on a line' do
let(:input) do
<<~ASCIIDOC
== Example
words image:example1.png[] words words image:example2.png[] words
ASCIIDOC
end
let(:expected_logs) do
<<~LOGS
INFO: <stdin>: line 3: copying #{spec_dir}/#{example1}
INFO: <stdin>: line 3: copying #{spec_dir}/#{example2}
LOGS
end
it 'copies the images' do
expect(copied).to eq(
[
['example1.png', "#{spec_dir}/#{example1}"],
['example2.png', "#{spec_dir}/#{example2}"],
]
)
end
it 'logs that it copied the image' do
expect(logs).to eq(expected_logs.strip)
end
end
context 'when the inline image has a specified width' do
let(:image_command) { "image:#{target}[width=600]" }
include_examples 'copies images with various paths'
end
context 'when the inline image is inside an ordered list' do
let(:image_command) { ". Words image:#{target}[] words" }
include_examples 'copies images with various paths'
end
context 'when the inline image is inside an unordered list' do
let(:image_command) { "* Words image:#{target}[] words" }
include_examples 'copies images with various paths'
end
context 'when the inline image is inside a definition list' do
let(:image_command) { "Foo:: Words image:#{target}[] words" }
include_examples 'copies images with various paths'
end
context 'when the inline image is inside a callout list' do
let(:image_command) do
<<~ASCIIDOC
----
foo <1>
----
<1> word image:#{target}[] word
ASCIIDOC
end
let(:log_offset) { 4 }
include_examples 'copies images with various paths'
end
context 'when there is a reference in an ordered list' do
let(:input) do
<<~ASCIIDOC
[[foo-thing]]
== Example
:id: foo
More words.
. <<{id}-thing>>
ASCIIDOC
end
it "doesn't log anything" do
expect(logs).to eq('')
end
end
context 'when there is an empty definition list' do
let(:input) do
<<~ASCIIDOC
== Example
Foo::
Bar:::
ASCIIDOC
end
it "doesn't log anything" do
expect(logs).to eq('')
end
end
end
context 'when the same image is referenced more than once' do
let(:input) do
<<~ASCIIDOC
== Example
image::#{example1}[]
image::#{example1}[]
image::#{example2}[]
image::#{example1}[]
image::#{example2}[]
ASCIIDOC
end
let(:expected_copied) do
[
[example1, "#{spec_dir}/#{example1}"],
[example2, "#{spec_dir}/#{example2}"],
]
end
it 'is only copied once' do
expect(copied).to eq(expected_copied)
end
let(:expected_logs) do
<<~LOG
INFO: <stdin>: line 2: copying #{spec_dir}/#{example1}
INFO: <stdin>: line 4: copying #{spec_dir}/#{example2}
LOG
end
it 'is only logged once' do
expect(logs).to eq(expected_logs.strip)
end
end
end