text:
package org.sahli.asciidoc.confluence.publisher.converter;

import org.hamcrest.Description;
import org.hamcrest.TypeSafeMatcher;
import org.junit.ClassRule;
import org.junit.Rule;
import org.junit.Test;
import org.junit.rules.ExpectedException;
import org.junit.rules.TemporaryFolder;
import org.sahli.asciidoc.confluence.publisher.converter.AsciidocPagesStructureProvider.AsciidocPage;

import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import static java.nio.charset.StandardCharsets.ISO_8859_1;
import static java.nio.charset.StandardCharsets.UTF_8;
import static java.nio.file.Files.copy;
import static java.nio.file.Files.createDirectories;
import static java.nio.file.Files.exists;
import static java.nio.file.Files.walk;
import static java.nio.file.Files.write;
import static java.nio.file.StandardCopyOption.REPLACE_EXISTING;
import static java.util.Collections.emptyList;
import static java.util.Collections.emptyMap;
import static org.apache.commons.codec.digest.DigestUtils.sha256Hex;
import static org.hamcrest.Matchers.containsString;
import static org.hamcrest.Matchers.hasEntry;
import static org.hamcrest.Matchers.hasItem;
import static org.hamcrest.Matchers.is;
import static org.junit.Assert.assertThat;
import static org.junit.rules.ExpectedException.none;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import static org.sahli.asciidoc.confluence.publisher.converter.AsciidocConfluencePage.newAsciidocConfluencePage;
import static org.sahli.asciidoc.confluence.publisher.converter.AsciidocConfluencePageTest.RootCauseMatcher.rootCauseWithMessage;

/**
 * @author Alain Sahli
 * @author Christian Stettler
 */
public class AsciidocConfluencePageTest {

    private static final Path TEMPLATES_FOLDER = Paths.get("src/main/resources/org/sahli/asciidoc/confluence/publisher/converter/templates");

    @ClassRule
    public static final GraphvizInstallationCheck GRAPHVIZ_INSTALLATION_CHECK = new GraphvizInstallationCheck();

    @ClassRule
    public static final TemporaryFolder TEMPORARY_FOLDER = new TemporaryFolder();

    @Rule
    public final ExpectedException expectedException = none();

    @Test
    public void render_asciidocWithTopLevelHeader_returnsConfluencePageWithPageTitleFromTopLevelHeader() {
        // arrange
        String adoc = "= Page title";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciiDocConfluencePage.pageTitle(), is("Page title"));
    }

    @Test
    public void render_asciidocWithTopLevelHeaderAndDocumentAttribute_returnsConfluencePageWithPageTitleFromTopLevelHeaderAndDocumentAttributeResolved() {
        // arrange
        String adoc = ":attributeWithValue: test\n" +
                ":attributeWithoutValue:\n" +
                "= Page title ({attributeWithValue}{attributeWithoutValue}{unknownAttribute})";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciiDocConfluencePage.pageTitle(), is("Page title (test{unknownAttribute})"));
    }

    @Test
    public void render_asciidocWithTopLevelHeaderAndUserAttribute_returnsConfluencePageWithPageTitleFromTopLevelHeaderAndUserAttributeResolved() {
        // arrange
        String adoc = "= Page title ({attributeWithValue}{attributeWithoutValue}{unknownAttribute})";

        Map<String, Object> userAttributes = new HashMap<>();
        userAttributes.put("attributeWithValue", "test");
        userAttributes.put("attributeWithoutValue", null);

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath(), userAttributes);

        // assert
        assertThat(asciiDocConfluencePage.pageTitle(), is("Page title (test{unknownAttribute})"));
    }

    @Test
    public void render_asciidocWithTitleMetaInformation_returnsConfluencePageWithPageTitleFromTitleMetaInformation() {
        // arrange
        String adoc = ":title: Page title (meta)";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciiDocConfluencePage.pageTitle(), is("Page title (meta)"));
    }

    @Test
    public void render_asciidocWithTitleMetaInformationAndDocumentAttribute_returnsConfluencePageWithPageTitleFromTitleMetaInformationAndDocumentAttributeResolved() {
        // arrange
        String adoc = ":attributeWithValue: test\n" +
                ":attributeWithoutValue:\n" +
                ":title: Page title ({attributeWithValue}{attributeWithoutValue}{unknownAttribute})";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciiDocConfluencePage.pageTitle(), is("Page title (test{unknownAttribute})"));
    }

    @Test
    public void render_asciidocWithTitleMetaInformationAndUserAttribute_returnsConfluencePageWithPageTitleFromTitleMetaInformationAndUserAttributeResolved() {
        // arrange
        String adoc = ":title: Page title ({attributeWithValue}{attributeWithoutValue}{unknownAttribute})";

        Map<String, Object> userAttributes = new HashMap<>();
        userAttributes.put("attributeWithValue", "test");
        userAttributes.put("attributeWithoutValue", null);

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath(), userAttributes);

        // assert
        assertThat(asciiDocConfluencePage.pageTitle(), is("Page title (test{unknownAttribute})"));
    }

    @Test
    public void render_asciidocWithTopLevelHeaderAndMetaInformation_returnsConfluencePageWithPageTitleFromTitleMetaInformation() {
        // arrange
        String adoc = ":title: Page title (meta)\n" +
                "= Page Title (header)";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciiDocConfluencePage.pageTitle(), is("Page title (meta)"));
    }

    @Test
    public void render_asciidocWithTopLevelHeaderAndMetaInformationAndPageTitlePostProcessorConfigured_returnsConfluencePageWithPostProcessedPageTitleFromTitleMetaInformation() {
        // arrange
        String adoc = ":title: Page title (meta)\n" +
                "= Page Title (header)";

        PageTitlePostProcessor pageTitlePostProcessor = mock(PageTitlePostProcessor.class);
        when(pageTitlePostProcessor.process("Page title (meta)")).thenReturn("Post-Processed Page Title");

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath(), pageTitlePostProcessor);

        // assert
        assertThat(asciidocConfluencePage.pageTitle(), is("Post-Processed Page Title"));
    }

    @Test
    public void render_asciidocWithTopLevelHeaderWithHtmlCharacter_returnsConfluencePageWithPageTitleWithUnescapedHtmlCharacter() {
        // arrange
        String adoc = "= Page&title";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath(), emptyMap());

        // assert
        assertThat(asciiDocConfluencePage.pageTitle(), is("Page&title"));
    }

    @Test
    public void render_asciidocWithoutAttributes_returnsConfluencePageWithoutAttributes() {
        // arrange
        String adoc = prependTitle("Hello {user}");

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath(), emptyMap());

        // assert
        assertThat(asciiDocConfluencePage.content(), is("
Hello {user}
"));
    }

    @Test
    public void render_asciidocWithPageTitleAndPageTitlePostProcessorConfigured_returnsConfluencePageWithPostProcessedPageTitle() {
        // arrange
        String adoc = "= Page Title";
        PageTitlePostProcessor pageTitlePostProcessor = mock(PageTitlePostProcessor.class);
        when(pageTitlePostProcessor.process("Page Title")).thenReturn("Post-Processed Page Title");

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath(), pageTitlePostProcessor);

        // assert
        assertThat(asciidocConfluencePage.pageTitle(), is("Post-Processed Page Title"));
    }

    @Test
    public void render_asciidocWithNeitherTopLevelHeaderNorTitleMetaInformation_returnsConfluencePageWithPageTitleFromMetaInformation() {
        // arrange
        String adoc = "Content";

        // assert
        this.expectedException.expect(RuntimeException.class);
        this.expectedException.expectMessage("failed to create confluence page for asciidoc content in");
        this.expectedException.expect(rootCauseWithMessage("top-level heading or title meta information must be set"));

        // act
        newAsciidocConfluencePage(asciidocPage(adoc), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());
    }

    @Test
    public void renderConfluencePage_asciiDocWithAttributes_returnsConfluencePageContentWithReplacedAttributes() {
        // arrange
        String adocContent = prependTitle("{attributeWithValue}{attributeWithoutValue}{unknownAttribute}");

        Map<String, Object> userAttributes = new HashMap<>();
        userAttributes.put("attributeWithValue", "test");
        userAttributes.put("attributeWithoutValue", null);

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(adocContent), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath(), userAttributes);

        // assert
        assertThat(asciidocConfluencePage.content(), is("
test{unknownAttribute}
"));
    }

    @Test
    public void renderConfluencePage_asciiDocWithListing_returnsConfluencePageContentWithMacroWithNameNoFormat() {
        // arrange
        String adocContent = "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"noformat\">" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithListingAndTitle_returnsConfluencePageContentWithMacroWithNameNoFormatAndTitle() {
        // arrange
        String adocContent = ".A block title\n" +
                "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"noformat\">" +
                "<ac:parameter ac:name=\"title\">A block title</ac:parameter>" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListing_returnsConfluencePageContentWithMacroWithNameCode() {
        // arrange
        String adocContent = "[source]\n" +
                "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithJavaSourceListing_returnsConfluencePageContentWithMacroWithJavaParameter() {
        // arrange
        String adocContent = "[source,java]\n" +
                "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:parameter ac:name=\"language\">java</ac:parameter>" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListingWithLineNumbers_returnsConfluencePageContentWithMacroWithLineNumbersParameter() {
        // arrange
        String adocContent = "[source%linenums]\n" +
                "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:parameter ac:name=\"linenumbers\">true</ac:parameter>" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListingWithLineNumbersAndJavaLanguage_returnsConfluencePageContentWithMacroWithLineNumbersAndJavaParameter() {
        // arrange
        String adocContent = "[source,java,linenums]\n" +
                "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:parameter ac:name=\"language\">java</ac:parameter>" +
                "<ac:parameter ac:name=\"linenumbers\">true</ac:parameter>" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListingWithLineNumbersAndStartIndex_returnsConfluencePageContentWithMacroWithLineNumbersAndFirstLineParameter() {
        // arrange
        String adocContent = "[source%linenums,start=3]\n" +
                "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:parameter ac:name=\"linenumbers\">true</ac:parameter>" +
                "<ac:parameter ac:name=\"firstline\">3</ac:parameter>" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListingWithGlobalLineNumbersAttribute_returnsConfluencePageContentWithMacroWithLineNumbersParameter() {
        // arrange
        String adocContent = ":source-linenums-option:\n" +
                "[source]\n" +
                "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:parameter ac:name=\"linenumbers\">true</ac:parameter>" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListingOfUnsupportedLanguage_returnsConfluencePageContentWithMacroWithoutLanguageElement() {
        // arrange
        String adocContent = "[source,unsupported]\n" +
                "----\n" +
                "GET /events?param1=value1&param2=value2 HTTP/1.1\n" +
                "Host: localhost:8080\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:plain-text-body>" +
                "<![CDATA[GET /events?param1=value1&param2=value2 HTTP/1.1\nHost: localhost:8080]]>" +
                "</ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithListingWithHtmlMarkup_returnsConfluencePageContentWithMacroWithoutHtmlEscape() {
        // arrange
        String adocContent = "----\n" +
                "line one\n" +
                "line two\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"noformat\">" +
                "<ac:plain-text-body><![CDATA[line one\nline two]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListingWithHtmlContent_returnsConfluencePageContentWithoutHtmlEscape() {
        // arrange
        String adocContent = "[source]\n" +
                "----\n" +
                "content with html\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:plain-text-body><![CDATA[content with html]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListingWithRegularExpressionSymbols_returnsConfluencePageContentWithRegularExpressionSymbolsEscaped() {
        // arrange
        String adocContent = "[source]\n" +
                "----\n" +
                "[0-9][0-9]\\.[0-9][0-9]\\.[0-9]{4}$\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:plain-text-body><![CDATA[[0-9][0-9]\\.[0-9][0-9]\\.[0-9]{4}$]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListingAndSourceHighlighter_returnsConfluencePageContentWithoutSourceHighlighting() {
        // arrange
        String adocContent = ":source-highlighter: coderay\n" +
                "[source,java]\n" +
                "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:parameter ac:name=\"language\">java</ac:parameter>" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListingWithCollapseOptionSetToTrue_returnsConfluencePageContentWithCodeMacroWithCollapsedOption() {
        // arrange
        String adocContent = "[source,collapse=true]\n" +
                "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:parameter ac:name=\"collapse\">true</ac:parameter>" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithSourceListingWithCollapseOptionSetToFalse_returnsConfluencePageContentWithCodeMacroWithoutCollapsedOption() {
        // arrange
        String adocContent = "[source,collapse=false]\n" +
                "----\n" +
                "import java.util.List;\n" +
                "----";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"code\">" +
                "<ac:plain-text-body><![CDATA[import java.util.List;]]></ac:plain-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithExampleBlock_returnsConfluencePageContentWithTipAdmonition() {
        // arrange
        String adocContent = "====\n" +
                "Content\n" +
                "====";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"tip\">" +
                "<ac:rich-text-body>
Content
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithExampleBlockWithTitle_returnsConfluencePageContentWithTipAdmonitionWithTitle() {
        // arrange
        String adocContent = ".Title\n" +
                "====\n" +
                "Content\n" +
                "====";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"tip\">" +
                "<ac:parameter ac:name=\"title\">Title</ac:parameter>" +
                "<ac:rich-text-body>
Content
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithCollapsibleExampleBlock_returnsConfluencePageContentWithExpandMacro() {
        // arrange
        String adocContent = "[%collapsible]\n" +
                "====\n" +
                "Collapsed Content\n" +
                "====";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"expand\">" +
                "<ac:rich-text-body>
Collapsed Content
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithCollapsibleExampleBlockWithTitle_returnsConfluencePageContentWithExpandMacroAndTitle() {
        // arrange
        String adocContent = ".Title\n" +
                "[%collapsible]\n" +
                "====\n" +
                "Collapsed Content\n" +
                "====";

        // act
        AsciidocConfluencePage asciiDocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"expand\">" +
                "<ac:parameter ac:name=\"title\">Title</ac:parameter>" +
                "<ac:rich-text-body>
Collapsed Content
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciiDocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithAllPossibleSectionLevels_returnsConfluencePageContentWithAllSectionHavingCorrectMarkup() {
        // arrange
        String adocContent = "= Title level 0\n\n" +
                "== Title level 1\n" +
                "=== Title level 2\n" +
                "==== Title level 3\n" +
                "===== Title level 4\n" +
                "====== Title level 5";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">_title_level_1</ac:parameter></ac:structured-macro>Title level 1
" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">_title_level_2</ac:parameter></ac:structured-macro>Title level 2
" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">_title_level_3</ac:parameter></ac:structured-macro>Title level 3
" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">_title_level_4</ac:parameter></ac:structured-macro>Title level 4
" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">_title_level_5</ac:parameter></ac:structured-macro>Title level 5
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithParagraph_returnsConfluencePageContentHavingCorrectParagraphMarkup() {
        // arrange
        String adoc = "some paragraph";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adoc)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
some paragraph
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithBoldText_returnsConfluencePageContentWithBoldMarkup() {
        // arrange
        String adocContent = "*Bold phrase.* bold le**t**ter.";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
Bold phrase. bold letter.
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithBr_returnsConfluencePageContentWithXhtml() {
        // arrange
        String adocContent = "a +\nb +\nc";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
a
\nb
\nc
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithItalicText_returnsConfluencePageContentWithItalicMarkup() {
        // arrange
        String adocContent = "_Italic phrase_ italic le__t__ter.";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
Italic phrase italic letter.
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithImageWithHeightAndWidthAttributeSurroundedByLink_returnsConfluencePageContentWithImageWithHeightAttributeMacroWrappedInLink() {
        // arrange
        String adocContent = "image::sunset.jpg[Sunset, 300, 200, link=\"http://www.foo.ch\"]";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:image ac:height=\"200\" ac:width=\"300\"><ri:attachment ri:filename=\"sunset.jpg\"></ri:attachment></ac:image>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithImageWithTitle_returnsConfluencePageContentWithImageWithTitle() {
        String adocContent = ".A beautiful sunset\n" +
                "image::sunset.jpg[]";

        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        String expectedContent = "<ac:image ac:title=\"A beautiful sunset\" ac:alt=\"A beautiful sunset\"><ri:attachment ri:filename=\"sunset.jpg\"></ri:attachment></ac:image>
Figure 1. A beautiful sunset
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithImage_returnsConfluencePageContentWithImage() {
        // arrange
        String adocContent = "image::sunset.jpg[]";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:image><ri:attachment ri:filename=\"sunset.jpg\"></ri:attachment></ac:image>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithImageInDifferentFolder_returnsConfluencePageContentWithImageAttachmentFileNameOnly() {
        // arrange
        String adocContent = "image::sub-folder/sunset.jpg[]";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:image><ri:attachment ri:filename=\"sunset.jpg\"></ri:attachment></ac:image>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithoutTableWithHeader_returnsConfluencePageContentWithTableWithoutHeader() {
        // arrange
        String adocContent = "" +
                "[cols=\"3*\"]\n" +
                "|===\n" +
                "| A\n" +
                "| B\n" +
                "| C\n" +
                "\n" +
                "| 10\n" +
                "| 11\n" +
                "| 12\n" +
                "\n" +
                "| 20\n" +
                "| 21\n" +
                "| 22\n" +
                "|===";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "

A	B	C

10	11	12

20	21	22

";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithTableWithHeader_returnsConfluencePageContentWithTableWithHeader() {
        // arrange
        String adocContent = "" +
                "[cols=\"3*\", options=\"header\"]\n" +
                "|===\n" +
                "| A\n" +
                "| B\n" +
                "| C\n" +
                "\n" +
                "| 10\n" +
                "| 11\n" +
                "| 12\n" +
                "\n" +
                "| 20\n" +
                "| 21\n" +
                "| 22\n" +
                "|===";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "

A	B	C

10	11	12

20	21	22

";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithTableWithRowSpan_returnsConfluencePageWithTableWithRowSpan() {
        // arrange
        String adocContent = "" +
                "[cols=\"3*\", options=\"header\"]\n" +
                "|===\n" +
                "| A\n" +
                "| B\n" +
                "| C\n" +
                "\n" +
                ".2+| 10\n" +
                "| 11\n" +
                "| 12\n" +
                "| 13\n" +
                "| 14\n" +
                "|===";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage));

        // assert
        String expectedContent = "

A	B	C

10	11	12

13	14

";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithTableWithColSpan_returnsConfluencePageWithTableWithColSpan() {
        // arrange
        String adocContent = "" +
                "[cols=\"3*\", options=\"header\"]\n" +
                "|===\n" +
                "| A\n" +
                "| B\n" +
                "| C\n" +
                "\n" +
                "| 10\n" +
                "2+| 11 & 12\n" +
                "\n" +
                "|===";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage));

        // assert
        String expectedContent = "

A	B	C

10	11 & 12

";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithTableWithAsciiDocCell_returnsConfluencePageWithTableWithAsciiDocCell() {
        // arrange
        String adocContent = "" +
                "|===\n" +
                "| A " +
                "| B\n" +
                "\n" +
                "| 10 " +
                "a|11\n" +
                "\n" +
                "* 12 \n" +
                "* 13 \n" +
                "\n" +
                "|===";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage));

        // assert
        String expectedContent = "

A	B

10

11
\n

12

13

";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithNoteContent_returnsConfluencePageContentWithInfoMacroWithContent() {
        // arrange
        String adocContent = "[NOTE]\n" +
                "====\n" +
                "Some note.\n" +
                "====";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"info\">" +
                "<ac:rich-text-body>
Some note.
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithNoteContentAndTitle_returnsConfluencePageContentWithInfoMacroWithContentAndTitle() {
        // arrange
        String adocContent = "[NOTE]\n" +
                ".Note Title\n" +
                "====\n" +
                "Some note.\n" +
                "====";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"info\">" +
                "<ac:parameter ac:name=\"title\">Note Title</ac:parameter>" +
                "<ac:rich-text-body>
Some note.
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithTipContent_returnsConfluencePageContentWithTipMacroWithContent() {
        // arrange
        String adocContent = "[TIP]\n" +
                "====\n" +
                "Some tip.\n" +
                "====";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"tip\">" +
                "<ac:rich-text-body>
Some tip.
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithTipContentAndTitle_returnsConfluencePageContentWithTipMacroWithContentAndTitle() {
        // arrange
        String adocContent = "[TIP]\n" +
                ".Tip Title\n" +
                "====\n" +
                "Some tip.\n" +
                "====";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"tip\">" +
                "<ac:parameter ac:name=\"title\">Tip Title</ac:parameter>" +
                "<ac:rich-text-body>
Some tip.
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithCautionContent_returnsConfluencePageContentWithNoteMacroWithContent() {
        // arrange
        String adocContent = "[CAUTION]\n" +
                "====\n" +
                "Some caution.\n" +
                "====";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"note\">" +
                "<ac:rich-text-body>
Some caution.
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithCautionContentAndTitle_returnsConfluencePageContentWithNoteMacroWithContentAndTitle() {
        // arrange
        String adocContent = "[CAUTION]\n" +
                ".Caution Title\n" +
                "====\n" +
                "Some caution.\n" +
                "====";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"note\">" +
                "<ac:parameter ac:name=\"title\">Caution Title</ac:parameter>" +
                "<ac:rich-text-body>
Some caution.
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithWarningContent_returnsConfluencePageContentWithNoteMacroWithContent() {
        // arrange
        String adocContent = "[WARNING]\n" +
                "====\n" +
                "Some warning.\n" +
                "====";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"note\">" +
                "<ac:rich-text-body>
Some warning.
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithWarningContentAndTitle_returnsConfluencePageContentWithNoteMacroWithContentAndTitle() {
        // arrange
        String adocContent = "[WARNING]\n" +
                ".Warning Title\n" +
                "====\n" +
                "Some warning.\n" +
                "====";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"note\">" +
                "<ac:parameter ac:name=\"title\">Warning Title</ac:parameter>" +
                "<ac:rich-text-body>
Some warning.
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithImportantContent_returnsConfluencePageContentWithNoteMacroWithContent() {
        // arrange
        String adocContent = "[IMPORTANT]\n" +
                "====\n" +
                "Some important.\n" +
                "====";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"warning\">" +
                "<ac:rich-text-body>
Some important.
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithImportantContentAndTitle_returnsConfluencePageContentWithNoteMacroWithContentAndTitle() {
        // arrange
        String adocContent = "[IMPORTANT]\n" +
                ".Important Title\n" +
                "====\n" +
                "Some important.\n" +
                "====";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:structured-macro ac:name=\"warning\">" +
                "<ac:parameter ac:name=\"title\">Important Title</ac:parameter>" +
                "<ac:rich-text-body>
Some important.
</ac:rich-text-body>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInterDocumentCrossReference_returnsConfluencePageWithLinkToReferencedPageByPageTitle() {
        // arrange
        Path rootFolder = copyAsciidocSourceToTemporaryFolder("src/test/resources/inter-document-cross-references");
        AsciidocPage asciidocPage = asciidocPage(rootFolder, "source-page.adoc");

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage), "TEST");

        // assert
        String expectedContent = "
This is a <ac:link><ri:page ri:content-title=\"Target Page\" ri:space-key=\"TEST\"></ri:page>" +
                "<ac:plain-text-link-body><![CDATA[reference]]></ac:plain-text-link-body>" +
                "</ac:link> to the target page.
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInterDocumentCrossReferenceToTargetPageWithHtmlCharacterInTitle_returnsConfluencePageWithLinkToReferencedPageByPageTitle() {
        // arrange
        Path rootFolder = copyAsciidocSourceToTemporaryFolder("src/test/resources/inter-document-cross-references");
        AsciidocPage asciidocPage = asciidocPage(rootFolder, "source-page-with-reference-to-target-page-with-html-character-in-title.adoc");

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage), "TEST");

        // assert
        String expectedContent = "
This is a <ac:link><ri:page ri:content-title=\"Target&Page\" ri:space-key=\"TEST\"></ri:page>" +
                "<ac:plain-text-link-body><![CDATA[reference]]></ac:plain-text-link-body>" +
                "</ac:link> to the target page.
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithCircularInterDocumentCrossReference_returnsConfluencePagesWithLinkToReferencedPageByPageTitle() {
        // arrange
        AsciidocPage asciidocPageOne = asciidocPage(Paths.get("src/test/resources/circular-inter-document-cross-references/page-one.adoc"));
        AsciidocPage asciidocPageTwo = asciidocPage(Paths.get("src/test/resources/circular-inter-document-cross-references/page-two.adoc"));

        // act
        AsciidocConfluencePage asciidocConfluencePageOne = newAsciidocConfluencePage(asciidocPageOne, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPageOne), "TEST");
        AsciidocConfluencePage asciidocConfluencePageTwo = newAsciidocConfluencePage(asciidocPageTwo, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPageTwo), "TEST");

        // assert
        assertThat(asciidocConfluencePageOne.content(), containsString("<ri:page ri:content-title=\"Page Two\" ri:space-key=\"TEST\">"));
        assertThat(asciidocConfluencePageTwo.content(), containsString("<ri:page ri:content-title=\"Page One\" ri:space-key=\"TEST\">"));
    }

    @Test
    public void renderConfluencePage_asciiDocWithLinkToAttachmentWithoutLinkText_returnsConfluencePageWithLinkToAttachmentAndAttachmentNameAsLinkText() {
        // arrange
        String adocContent = "link:foo.txt[]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
<ac:link><ri:attachment ri:filename=\"foo.txt\"></ri:attachment></ac:link>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithLinkToAttachmentWithLinkText_returnsConfluencePageWithLinkToAttachmentAndSpecifiedLinkText() {
        // arrange
        String adocContent = "link:foo.txt[Bar]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
<ac:link><ri:attachment ri:filename=\"foo.txt\"></ri:attachment><ac:plain-text-link-body><![CDATA[Bar]]></ac:plain-text-link-body></ac:link>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInclude_returnsConfluencePageWithContentFromIncludedPage() {
        // arrange
        Path rootFolder = copyAsciidocSourceToTemporaryFolder("src/test/resources/includes");
        AsciidocPage asciidocPage = asciidocPage(rootFolder, "page.adoc");

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage));

        // assert
        assertThat(asciidocConfluencePage.content(), containsString("
main content
"));
        assertThat(asciidocConfluencePage.content(), containsString("
included content
"));
    }

    @Test
    public void renderConfluencePage_asciiDocWithUtf8CharacterInTitle_returnsConfluencePageWithCorrectlyEncodedUtf8CharacterInTitle() {
        try {
            // arrange
            setDefaultCharset(ISO_8859_1);

            String adocContent = "= Title © !";
            AsciidocPage asciidocPage = asciidocPage(adocContent);

            // act
            AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage));

            // assert
            assertThat(asciidocConfluencePage.pageTitle(), is("Title © !"));
        } finally {
            setDefaultCharset(UTF_8);
        }
    }

    @Test
    public void renderConfluencePage_asciiDocWithUtf8CharacterInContent_returnsConfluencePageWithCorrectlyEncodedUtf8CharacterInContent() {
        try {
            // arrange
            setDefaultCharset(ISO_8859_1);

            String adocContent = "Copyrighted content © !";
            AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

            // act
            AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage));

            // assert
            assertThat(asciidocConfluencePage.content(), is("
Copyrighted content © !
"));
        } finally {
            setDefaultCharset(UTF_8);
        }
    }

    @Test
    public void renderConfluencePage_asciiDocWithIsoEncodingAndSpecificSourceEncodingConfigured_returnsConfluencePageWithCorrectlyEncodedContent() {
        // arrange
        AsciidocPage asciidocPage = asciidocPage(Paths.get("src/test/resources/encoding/iso-encoded-source.adoc"));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, ISO_8859_1, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage));

        // assert
        assertThat(asciidocConfluencePage.content(), is("
This line contains an ISO-8859-1 encoded special character 'À'.
"));
    }

    @Test
    public void renderConfluencePage_asciiDocWithLinkToAttachmentInDifferentFolder_returnsConfluencePageWithLinkToAttachmentFileNameOnly() {
        // arrange
        String adocContent = "link:bar/foo.txt[]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
<ac:link><ri:attachment ri:filename=\"foo.txt\"></ri:attachment></ac:link>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithExplicitExternalLinkAndLinkText_returnsConfluencePageWithLinkToExternalPageAndSpecifiedLinkText() {
        // arrange
        String adocContent = "link:http://www.google.com[Google]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
Google
";
        assertThat(asciidocConfluencePage.content(), containsString(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithExternalLinkWithoutLinkText_returnsConfluencePageWithLinkToExternalPageAndUrlAsLinkText() {
        // arrange
        String adocContent = "link:http://www.google.com[]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
http://www.google.com
";
        assertThat(asciidocConfluencePage.content(), containsString(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithImplicitExternalLink_returnsConfluencePageWithLinkToExternalPageAndUrlAsLinkText() {
        // arrange
        String adocContent = "http://www.google.com";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
http://www.google.com
";
        assertThat(asciidocConfluencePage.content(), containsString(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithEmbeddedPlantUmlDiagram_returnsConfluencePageWithLinkToGeneratedPlantUmlImage() {
        // arrange
        String adocContent = "[plantuml, embedded-diagram, png]\n" +
                "....\n" +
                "A <|-- B\n" +
                "....";

        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage));

        // assert
        String expectedContent = "<ac:image ac:height=\"176\" ac:width=\"60\"><ri:attachment ri:filename=\"embedded-diagram.png\"></ri:attachment></ac:image>";
        assertThat(asciidocConfluencePage.content(), containsString(expectedContent));
        assertThat(exists(assetsTargetFolderFor(asciidocPage).resolve("embedded-diagram.png")), is(true));
    }

    @Test
    public void renderConfluencePage_asciiDocWithIncludedPlantUmlFile_returnsConfluencePageWithLinkToGeneratedPlantUmlImage() {
        // arrange
        Path rootFolder = copyAsciidocSourceToTemporaryFolder("src/test/resources/plantuml");
        AsciidocPage asciidocPage = asciidocPage(rootFolder, "page.adoc");

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, assetsTargetFolderFor(asciidocPage));

        // assert
        String expectedContent = "<ac:image ac:height=\"176\" ac:width=\"60\"><ri:attachment ri:filename=\"included-diagram.png\"></ri:attachment></ac:image>";
        assertThat(asciidocConfluencePage.content(), containsString(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithUnorderedList_returnsConfluencePageHavingCorrectUnorderedListMarkup() {
        // arrange
        String adocContent = "* L1-1\n" +
                "** L2-1\n" +
                "*** L3-1\n" +
                "**** L4-1\n" +
                "***** L5-1\n" +
                "* L1-2";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "

L1-1

L2-1

L3-1

L4-1

L5-1

L1-2

";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithUnorderedListAndTitle_returnsConfluencePageHavingCorrectUnorderedListAndTitleMarkup() {
        // arrange
        String adocContent = ".An unordered list title\n" +
                "* L1-1\n" +
                "* L1-2\n";

        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
An unordered list title
" + "

L1-1

L1-2

";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithOrderedList_returnsConfluencePageHavingCorrectOrderedListMarkup() {
        // arrange
        String adocContent = ". L1-1\n" +
                ".. L2-1\n" +
                "... L3-1\n" +
                ".... L4-1\n" +
                "..... L5-1\n" +
                ". L1-2";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "

L1-1

L2-1

L3-1

L4-1

L5-1

L1-2

";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithOrderedListAndTitle_returnsConfluencePageHavingCorrectOrderedListAndTitleMarkup() {
        // arrange
        String adocContent = ".An ordered list title\n" +
                ". L1-1\n" +
                ". L1-2\n";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
An ordered list title

L1-1

L1-2

";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInlineImage_returnsConfluencePageWithInlineImage() {
        // arrange
        String adocContent = "Some text image:sunset.jpg[] with inline image";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
Some text <ac:image ac:alt=\"sunset\"><ri:attachment ri:filename=\"sunset.jpg\"></ri:attachment></ac:image> with inline image
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInlineRemoteImage_returnsConfluencePageWithInlineRemoteImage() {
        // arrange
        String adocContent = "Some text image:https://asciidoctor.org/images/octocat.jpg[GitHub mascot]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
Some text " +
                "<ac:image ac:alt=\"GitHub mascot\">" +
                "<ri:url ri:value=\"https://asciidoctor.org/images/octocat.jpg\"></ri:url>" +
                "</ac:image>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
        assertThat(asciidocConfluencePage.attachments().size(), is(0));
    }

    @Test
    public void renderConfluencePage_asciiDocWithBlockRemoteImageAndBlockTitle_returnsConfluencePageWithBlockRemoteImageAndImageCaption() {
        String adocContent = ".GitHub mascot\n" +
                "image::https://asciidoctor.org/images/octocat.jpg[]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "<ac:image ac:title=\"GitHub mascot\" ac:alt=\"GitHub mascot\">" +
                "<ri:url ri:value=\"https://asciidoctor.org/images/octocat.jpg\"></ri:url>" +
                "</ac:image>" +
                "
Figure 1. GitHub mascot
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
        assertThat(asciidocConfluencePage.attachments().size(), is(0));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInlineImageWithHeightAndWidthAttributeSurroundedByLink_returnsConfluencePageContentWithInlineImageWithHeightAttributeMacroWrappedInLink() {
        // arrange
        String adocContent = "Some text image:sunset.jpg[Sunset, 16, 20, link=\"http://www.foo.ch\"] with inline image";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
Some text <ac:image ac:height=\"20\" ac:width=\"16\" ac:alt=\"Sunset\"><ri:attachment ri:filename=\"sunset.jpg\"></ri:attachment></ac:image> with inline image
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInlineImageInDifferentFolder_returnsConfluencePageContentWithInlineImageAttachmentFileNameOnly() {
        // arrange
        String adocContent = "Some text image:sub-folder/sunset.jpg[] with inline image";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "
Some text <ac:image ac:alt=\"sunset\"><ri:attachment ri:filename=\"sunset.jpg\"></ri:attachment></ac:image> with inline image
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInternalCrossReferenceToSectionWithBlockAnchor_returnsConfluencePageContentWithInternalCrossReferenceToSectionUsingBlockAnchor() {
        // arrange
        String adocContent = "" +
                "[[section-1]]\n" +
                "== Section 1\n" +
                "Cross reference to <
>";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">section-1</ac:parameter></ac:structured-macro>Section 1
" +
                "
Cross reference to <ac:link ac:anchor=\"section-1\"><ac:plain-text-link-body><![CDATA[Section 1]]></ac:plain-text-link-body></ac:link>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInternalCrossReferenceToSectionWithCustomId_returnsConfluencePageContentWithInternalCrossReferenceToSectionUsingCustomId() {
        // arrange
        String adocContent = "" +
                "[#section-1]\n" +
                "== Section 1\n" +
                "Cross reference to <
>";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">section-1</ac:parameter></ac:structured-macro>Section 1
" +
                "
Cross reference to <ac:link ac:anchor=\"section-1\"><ac:plain-text-link-body><![CDATA[Section 1]]></ac:plain-text-link-body></ac:link>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInternalCrossReferenceInlineToSectionWithInlineAnchor_returnsConfluencePageContentWithInternalCrossReferenceToSectionUsingSectionTitle() {
        // arrange
        String adocContent = "" +
                "== Section 1 [[section-1]]\n" +
                "Cross reference to <
>";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">section-1</ac:parameter></ac:structured-macro>Section 1
" +
                "
Cross reference to <ac:link ac:anchor=\"section-1\"><ac:plain-text-link-body><![CDATA[Section 1]]></ac:plain-text-link-body></ac:link>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInternalCrossReferenceToSectionAndCustomLabel_returnsConfluencePageContentWithInternalCrossReferenceToSectionUsingCustomLabel() {
        // arrange
        String adocContent = "" +
                "== Section 1 [[section-1]]\n" +
                "Cross reference to <
>";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">section-1</ac:parameter></ac:structured-macro>Section 1
" +
                "
Cross reference to <ac:link ac:anchor=\"section-1\"><ac:plain-text-link-body><![CDATA[section 1]]></ac:plain-text-link-body></ac:link>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInternalCrossReferenceToParagraph_returnsConfluencePageContentWithInternalCrossReferenceToParagraphUsingAnchorId() {
        // arrange
        String adocContent = "" +
                "[[paragraph1]]Paragraph\n\n" +
                "Cross reference to <<paragraph1>>";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">paragraph1</ac:parameter></ac:structured-macro>Paragraph
\n" +
                "
Cross reference to <ac:link ac:anchor=\"paragraph1\"><ac:plain-text-link-body><![CDATA[[paragraph1]]]></ac:plain-text-link-body></ac:link>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInternalCrossReferenceToParagraphAndCustomLabel_returnsConfluencePageContentWithInternalCrossReferenceToParagraphUsingCustomLabel() {
        // arrange
        String adocContent = "" +
                "[[paragraph1]]Paragraph\n\n" +
                "Cross reference to <<paragraph1,Paragraph>>";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">paragraph1</ac:parameter></ac:structured-macro>Paragraph
\n" +
                "
Cross reference to <ac:link ac:anchor=\"paragraph1\"><ac:plain-text-link-body><![CDATA[Paragraph]]></ac:plain-text-link-body></ac:link>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithInternalCrossReferenceToBibliographyAnchor_returnsConfluencePageContentWithInternalCrossReferenceToBibliographyAnchor() {
        // arrange
        String adocContent = "" +
                "[bibliography]\n" +
                "== References\n\n" +
                "* [[[pp]]] Entry1\n\n" +
                "* [[[gof,gang]]] Entry2\n\n" +
                "Cross reference to <<pp>> and <<gof>>";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(prependTitle(adocContent)), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">_references</ac:parameter></ac:structured-macro>References

" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">pp</ac:parameter></ac:structured-macro>[pp] Entry1
" +
                "
<ac:structured-macro ac:name=\"anchor\"><ac:parameter ac:name=\"\">gof</ac:parameter></ac:structured-macro>[gof] Entry2

\n" +
                "
Cross reference to <ac:link ac:anchor=\"pp\"><ac:plain-text-link-body><![CDATA[[pp]]]></ac:plain-text-link-body></ac:link>" +
                " and <ac:link ac:anchor=\"gof\"><ac:plain-text-link-body><![CDATA[[gang]]]></ac:plain-text-link-body></ac:link>
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithTableOfContentsAsAttribute_returnsConfluencePageContentWithTableOfContentsMacro() {
        // arrange
        String adocContent = "" +
                "= Page Title\n" +
                ":toc: auto\n";
        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(adocContent), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "
" +
                "<ac:structured-macro ac:name=\"toc\">" +
                "<ac:parameter ac:name=\"maxLevel\">2</ac:parameter>" +
                "</ac:structured-macro>" +
                "
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithTableOfContentsAsAttributeWithCustomDepth_returnsConfluencePageContentWithTableOfContentsMacroWithCustomDepth() {
        // arrange
        String adocContent = "" +
                "= Page Title\n" +
                ":toc: auto\n" +
                ":toclevels: 4\n";
        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(adocContent), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "
" +
                "<ac:structured-macro ac:name=\"toc\">" +
                "<ac:parameter ac:name=\"maxLevel\">4</ac:parameter>" +
                "</ac:structured-macro>" +
                "
";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithTableOfContentsAsMacro_returnsConfluencePageContentWithTableOfContentsMacro() {
        // arrange
        String adocContent = "" +
                "= Page Title\n" +
                ":toc: macro\n" +
                "\n" +
                "toc::[]";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(adocContent), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "<ac:structured-macro ac:name=\"toc\">" +
                "<ac:parameter ac:name=\"maxLevel\">2</ac:parameter>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void renderConfluencePage_asciiDocWithTableOfContentsAsMacroWithCustomDepth_returnsConfluencePageContentWithTableOfContentsMacroWithCustomDepth() {
        // arrange
        String adocContent = "" +
                "= Page Title\n" +
                ":toc: macro\n" +
                ":toclevels: 4\n" +
                "\n" +
                "toc::[]";

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage(adocContent), UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        String expectedContent = "" +
                "<ac:structured-macro ac:name=\"toc\">" +
                "<ac:parameter ac:name=\"maxLevel\">4</ac:parameter>" +
                "</ac:structured-macro>";
        assertThat(asciidocConfluencePage.content(), is(expectedContent));
    }

    @Test
    public void attachments_asciiDocWithImage_returnsImageAsAttachmentWithPathAndName() {
        // arrange
        String adocContent = "image::sunset.jpg[]";

        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciidocConfluencePage.attachments().size(), is(1));
        assertThat(asciidocConfluencePage.attachments(), hasEntry("sunset.jpg", "sunset.jpg"));
    }

    @Test
    public void attachments_asciiDocWithImageInDifferentFolder_returnsImageAsAttachmentWithPathAndFileNameOnly() {
        // arrange
        String adocContent = "image::sub-folder/sunset.jpg[]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciidocConfluencePage.attachments().size(), is(1));
        assertThat(asciidocConfluencePage.attachments(), hasEntry("sub-folder/sunset.jpg", "sunset.jpg"));
    }

    @Test
    public void attachments_asciiDocWithMultipleLevelsAndImages_returnsAllAttachments() {
        // arrange
        String adocContent = "= Title 1\n\n" +
                "image::sunset.jpg[]\n" +
                "== Title 2\n" +
                "image::sunrise.jpg[]";
        AsciidocPage asciidocPage = asciidocPage(adocContent);

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciidocConfluencePage.attachments().size(), is(2));
        assertThat(asciidocConfluencePage.attachments(), hasEntry("sunset.jpg", "sunset.jpg"));
        assertThat(asciidocConfluencePage.attachments(), hasEntry("sunrise.jpg", "sunrise.jpg"));
    }

    @Test
    public void attachments_asciiDocWithMultipleTimesSameImage_returnsNoDuplicateAttachments() {
        // arrange
        String adocContent = "image::sunrise.jpg[]\n" +
                "image::sunrise.jpg[]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciidocConfluencePage.attachments().size(), is(1));
        assertThat(asciidocConfluencePage.attachments(), hasEntry("sunrise.jpg", "sunrise.jpg"));
    }

    @Test
    public void attachments_asciiDocWithLinkToAttachment_returnsAttachmentWithPathAndName() {
        // arrange
        String adocContent = "link:foo.txt[]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciidocConfluencePage.attachments().size(), is(1));
        assertThat(asciidocConfluencePage.attachments(), hasEntry("foo.txt", "foo.txt"));
    }

    @Test
    public void attachments_asciiDocWithLinkToAttachmentInDifferentFolder_returnsAttachmentWithPathAndFileNameOnly() {
        // arrange
        String adocContent = "link:sub-folder/foo.txt[]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciidocConfluencePage.attachments().size(), is(1));
        assertThat(asciidocConfluencePage.attachments(), hasEntry("sub-folder/foo.txt", "foo.txt"));
    }

    @Test
    public void attachments_asciiDocWithImageAndLinkToAttachment_returnsAllAttachments() {
        // arrange
        String adocContent = "image::sunrise.jpg[]\n" +
                "link:foo.txt[]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        assertThat(asciidocConfluencePage.attachments().size(), is(2));
        assertThat(asciidocConfluencePage.attachments(), hasEntry("sunrise.jpg", "sunrise.jpg"));
        assertThat(asciidocConfluencePage.attachments(), hasEntry("foo.txt", "foo.txt"));
    }

    @Test
    public void attachments_asciiDocWithAttachmentWithSpaceInName_returnsAttachmentWithPathAndName() {
        // arrange
        String adocContent = "link:attachment%20with%20space.txt[]";
        AsciidocPage asciidocPage = asciidocPage(prependTitle(adocContent));

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciidocConfluencePage.attachments().size(), is(1));
        assertThat(asciidocConfluencePage.attachments(), hasEntry("attachment with space.txt", "attachment with space.txt"));
    }

    @Test
    public void keywords_singleKeyword_returnsSingleKeyword() {
        // arrange
        String adoc = "= Page Title\n"
                + ":keywords: foo";

        AsciidocPage asciidocPage = asciidocPage(adoc);

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciidocConfluencePage.keywords().size(), is(1));
        assertThat(asciidocConfluencePage.keywords(), hasItem("foo"));
    }

    @Test
    public void keywords_multipleKeywords_returnAllKeywords() {
        // arrange
        String adoc = "= Page Title\n"
                + ":keywords: foo, bar";

        AsciidocPage asciidocPage = asciidocPage(adoc);

        // act
        AsciidocConfluencePage asciidocConfluencePage = newAsciidocConfluencePage(asciidocPage, UTF_8, TEMPLATES_FOLDER, dummyAssetsTargetPath());

        // assert
        assertThat(asciidocConfluencePage.keywords().size(), is(2));
        assertThat(asciidocConfluencePage.keywords(), hasItem("foo"));
        assertThat(asciidocConfluencePage.keywords(), hasItem("bar"));
    }

    private static String prependTitle(String content) {
        if (!content.startsWith("= ")) {
            content = "= Default Page Title\n\n" + content;
        }
        return content;
    }

    private static Path assetsTargetFolderFor(AsciidocPage asciidocPage) {
        return asciidocPage.path().getParent();
    }

    private static Path dummyAssetsTargetPath() {
        try {
            return TEMPORARY_FOLDER.newFolder().toPath();
        } catch (IOException e) {
            throw new RuntimeException("Could not create assert target path", e);
        }
    }

    private static Path copyAsciidocSourceToTemporaryFolder(String pathToSampleAsciidocStructure) {
        try {
            Path sourceFolder = Paths.get(pathToSampleAsciidocStructure);
            Path targetFolder = TEMPORARY_FOLDER.newFolder().toPath();

            walk(Paths.get(pathToSampleAsciidocStructure)).forEach((path) -> copyTo(path, targetFolder.resolve(sourceFolder.relativize(path))));

            return targetFolder;
        } catch (IOException e) {
            throw new RuntimeException("Could not copy sample asciidoc structure", e);
        }
    }

    private static void copyTo(Path sourcePath, Path targetPath) {
        try {
            createDirectories(targetPath.getParent());
            copy(sourcePath, targetPath, REPLACE_EXISTING);
        } catch (IOException e) {
            throw new RuntimeException("Could not copy source path to target path", e);
        }
    }

    private static Path temporaryPath(String content) {
        try {
            Path path = TEMPORARY_FOLDER.newFolder().toPath().resolve("tmp").resolve(sha256Hex(content) + ".adoc");
            createDirectories(path.getParent());
            write(path, content.getBytes(UTF_8));

            return path;
        } catch (IOException e) {
            throw new RuntimeException("Could not write content to temporary path", e);
        }
    }

    private AsciidocPage asciidocPage(Path rootFolder, String asciidocFileName) {
        return asciidocPage(rootFolder.resolve(asciidocFileName));
    }

    private AsciidocPage asciidocPage(Path contentPath) {
        return new TestAsciidocPage(contentPath);
    }

    private AsciidocPage asciidocPage(String content) {
        return asciidocPage(temporaryPath(content));
    }

    private static class TestAsciidocPage implements AsciidocPage {

        private final Path path;

        TestAsciidocPage(Path path) {
            this.path = path;
        }

        @Override
        public Path path() {
            return this.path;
        }

        @Override
        public List<AsciidocPage> children() {
            return emptyList();
        }

    }

    private static void setDefaultCharset(Charset charset) {
        try {
            Field defaultCharsetField = Charset.class.getDeclaredField("defaultCharset");
            defaultCharsetField.setAccessible(true);
            defaultCharsetField.set(null, charset);
        } catch (Exception e) {
            throw new RuntimeException("Could not set default charset", e);
        }
    }

    static class RootCauseMatcher extends TypeSafeMatcher<Exception> {

        private final String message;

        private RootCauseMatcher(String message) {
            this.message = message;
        }

        @Override
        protected boolean matchesSafely(Exception exception) {
            return exception.getCause().getMessage().equals(this.message);
        }

        @Override
        public void describeTo(Description description) {
            description.appendText("root cause with message '" + this.message + "'");
        }

        static RootCauseMatcher rootCauseWithMessage(String message) {
            return new RootCauseMatcher(message);
        }

    }

}


text:
package org.squiddev.plethora.gameplay.modules.glasses.objects.object2d;

import com.google.common.base.Objects;
import io.netty.buffer.ByteBuf;
import net.minecraft.client.renderer.BufferBuilder;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.squiddev.plethora.gameplay.modules.glasses.CanvasClient;
import org.squiddev.plethora.gameplay.modules.glasses.objects.ColourableObject;
import org.squiddev.plethora.gameplay.modules.glasses.objects.ObjectRegistry;
import org.squiddev.plethora.utils.ByteBufUtils;
import org.squiddev.plethora.utils.Vec2d;

import javax.annotation.Nonnull;
import java.util.ArrayList;

import static org.lwjgl.opengl.GL11.GL_TRIANGLES;

public class Polygon extends ColourableObject implements MultiPointResizable2D {
	protected final ArrayList<Vec2d> points = new ArrayList<>();

	protected Polygon(int id, int parent, byte type) {
		super(id, parent, type);
	}

	public Polygon(int id, int parent) {
		super(id, parent, ObjectRegistry.POLYGON_2D);
	}

	@Nonnull
	@Override
	public Vec2d getPoint(int idx) {
		return points.get(idx);
	}

	@Override
	public void setVertex(int idx, @Nonnull Vec2d point) {
		if (!Objects.equal(points.get(idx), point)) {
			points.set(idx, point);
			setDirty();
		}
	}

	@Override
	public int getVertices() {
		return points.size();
	}

	@Override
	public void removePoint(int idx) {
		points.remove(idx);
		setDirty();
	}

	@Override
	public void addPoint(int idx, @Nonnull Vec2d point) {
		if (idx == points.size()) {
			points.add(point);
		} else {
			points.add(idx, point);
		}

		setDirty();
	}

	@Override
	public void writeInitial(ByteBuf buf) {
		super.writeInitial(buf);
		buf.writeByte(points.size());

		for (Vec2d point : points) ByteBufUtils.writeVec2d(buf, point);
	}

	@Override
	public void readInitial(ByteBuf buf) {
		super.readInitial(buf);
		int count = buf.readUnsignedByte();
		points.ensureCapacity(count);

		if (points.size() > count) points.subList(count, points.size()).clear();

		for (int i = 0; i < count; i++) {
			Vec2d point = ByteBufUtils.readVec2d(buf);
			if (i < points.size()) {
				points.set(i, point);
			} else {
				points.add(point);
			}
		}
	}

	@Override
	@SideOnly(Side.CLIENT)
	public void draw(CanvasClient canvas) {
		if (points.size() < 3) return;

		setupFlat();

		int size = points.size();
		Vec2d a = points.get(0);

		int red = getRed(), green = getGreen(), blue = getBlue(), alpha = getAlpha();

		Tessellator tessellator = Tessellator.getInstance();
		BufferBuilder buffer = tessellator.getBuffer();
		buffer.begin(GL_TRIANGLES, DefaultVertexFormats.POSITION_COLOR);

		for (int i = 1; i < size - 1; i++) {
			Vec2d b = points.get(i), c = points.get(i + 1);
			buffer.pos(a.x, a.y, 0).color(red, green, blue, alpha).endVertex();
			buffer.pos(b.x, b.y, 0).color(red, green, blue, alpha).endVertex();
			buffer.pos(c.x, c.y, 0).color(red, green, blue, alpha).endVertex();
		}

		tessellator.draw();
	}
}

redpajama_set_name: RedPajamaGithub


text:
cask "lagrange" do
  version "1.6.5"

  if Hardware::CPU.intel?
    sha256 "b145ccaa33264d7f1453acda7b2957726bcf1a05d0115f7f0daf7479d3f44e5d"

    url "https://github.com/skyjake/lagrange/releases/download/v#{version}/lagrange_v#{version}_macos10.13-x86_64.tbz",
        verified: "github.com/skyjake/lagrange/"
  else
    sha256 "31e1774b53ec3df35751693c6d0d3ac63453b8ee1e5866a2ecd6e06a64bc312f"

    url "https://github.com/skyjake/lagrange/releases/download/v#{version}/lagrange_v#{version}_macos11.0-arm64.tbz",
        verified: "github.com/skyjake/lagrange/"
  end

  name "Lagrange"
  desc "Desktop GUI client for browsing Geminispace"
  homepage "https://gmi.skyjake.fi/lagrange/"

  depends_on macos: ">= :high_sierra"

  app "Lagrange.app"

  zap trash: [
    "~/Library/Application Support/fi.skyjake.Lagrange",
    "~/Library/Preferences/fi.skyjake.Lagrange.plist",
    "~/Library/Saved Application State/fi.skyjake.Lagrange.savedState",
  ]
end


text:
/**
 * @module index.js
 * @summary: Wires up the library.
 *
 * @description:
 *
 * Author: justin
 * Created On: 2015-03-21.
 * @license Apache-2.0
 */

module.exports = require('./src/robust-notification.js');