Compare commits
10 Commits
confluence
...
unit-testi
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6ab08dfbf7 | ||
|
|
035d825757 | ||
|
|
147d4f4ac0 | ||
|
|
7ea26df373 | ||
|
|
9f7f1d63be | ||
|
|
ffda180b16 | ||
|
|
4d5b8a159e | ||
|
|
5090d681b3 | ||
|
|
ad3937e5db | ||
|
|
bb8417e57b |
18
pom.xml
18
pom.xml
@@ -79,6 +79,24 @@
|
|||||||
<version>${atlassian.spring.scanner.version}</version>
|
<version>${atlassian.spring.scanner.version}</version>
|
||||||
<scope>provided</scope>
|
<scope>provided</scope>
|
||||||
</dependency>
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>javax.inject</groupId>
|
||||||
|
<artifactId>javax.inject</artifactId>
|
||||||
|
<version>1</version>
|
||||||
|
<scope>provided</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>org.mockito</groupId>
|
||||||
|
<artifactId>mockito-all</artifactId>
|
||||||
|
<version>1.9.0</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
|
<dependency>
|
||||||
|
<groupId>net.sourceforge.htmlunit</groupId>
|
||||||
|
<artifactId>htmlunit</artifactId>
|
||||||
|
<version>2.32</version>
|
||||||
|
<scope>test</scope>
|
||||||
|
</dependency>
|
||||||
</dependencies>
|
</dependencies>
|
||||||
<build>
|
<build>
|
||||||
<plugins>
|
<plugins>
|
||||||
|
|||||||
@@ -24,7 +24,6 @@ import com.atlassian.plugin.spring.scanner.annotation.imports.ComponentImport;
|
|||||||
import com.atlassian.webresource.api.assembler.PageBuilderService;
|
import com.atlassian.webresource.api.assembler.PageBuilderService;
|
||||||
import org.springframework.beans.factory.annotation.Autowired;
|
import org.springframework.beans.factory.annotation.Autowired;
|
||||||
|
|
||||||
|
|
||||||
import com.vladsch.flexmark.ast.Node;
|
import com.vladsch.flexmark.ast.Node;
|
||||||
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughSubscriptExtension;
|
import com.vladsch.flexmark.ext.gfm.strikethrough.StrikethroughSubscriptExtension;
|
||||||
import com.vladsch.flexmark.ext.tables.TablesExtension;
|
import com.vladsch.flexmark.ext.tables.TablesExtension;
|
||||||
@@ -41,7 +40,6 @@ import com.vladsch.flexmark.html.HtmlRenderer;
|
|||||||
import com.vladsch.flexmark.parser.Parser;
|
import com.vladsch.flexmark.parser.Parser;
|
||||||
import com.vladsch.flexmark.util.options.MutableDataSet;
|
import com.vladsch.flexmark.util.options.MutableDataSet;
|
||||||
|
|
||||||
|
|
||||||
import java.net.*;
|
import java.net.*;
|
||||||
import java.io.*;
|
import java.io.*;
|
||||||
|
|
||||||
@@ -79,10 +77,13 @@ public class MarkdownFromURLMacro extends BaseMacro implements Macro
|
|||||||
@Override
|
@Override
|
||||||
public String execute(Map<String, String> parameters, String bodyContent, ConversionContext conversionContext) throws MacroExecutionException
|
public String execute(Map<String, String> parameters, String bodyContent, ConversionContext conversionContext) throws MacroExecutionException
|
||||||
{
|
{
|
||||||
|
//If the macro was not left blank, run the macro. Otherwise, return an empty string.
|
||||||
if (bodyContent != null) {
|
if (bodyContent != null) {
|
||||||
|
//Include highlight.js in the webpage.
|
||||||
|
//Adds something like <script type="text/javascript" src="path-to-javascript-file.js"></script> to the <head> of the page
|
||||||
pageBuilderService.assembler().resources().requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs");
|
pageBuilderService.assembler().resources().requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs");
|
||||||
|
|
||||||
|
//Set options for flexmark. See flexmark documentation for more info.
|
||||||
MutableDataSet options = new MutableDataSet();
|
MutableDataSet options = new MutableDataSet();
|
||||||
|
|
||||||
options.set(Parser.EXTENSIONS, Arrays.asList(
|
options.set(Parser.EXTENSIONS, Arrays.asList(
|
||||||
@@ -100,26 +101,35 @@ public class MarkdownFromURLMacro extends BaseMacro implements Macro
|
|||||||
|
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
|
//JavaScript for syntax highlighting. See highlight.js documentation for more info.
|
||||||
String highlightjs = "<script>\n" +
|
String highlightjs = "<script>\n" +
|
||||||
"AJS.$('[data-macro-name=\"markdown\"] code').each(function(i, block) {\n" +
|
"AJS.$('[data-macro-name=\"markdown\"] code').each(function(i, block) {\n" +
|
||||||
" hljs.highlightBlock(block);\n" +
|
" hljs.highlightBlock(block);\n" +
|
||||||
" });\n" +
|
" });\n" +
|
||||||
"</script>";
|
"</script>";
|
||||||
|
|
||||||
|
//Define a custom exception for trying to import from a private Bitbucket repository
|
||||||
class privateRepositoryException extends Exception {
|
class privateRepositoryException extends Exception {
|
||||||
public privateRepositoryException(String message) {
|
public privateRepositoryException(String message) {
|
||||||
super(message);
|
super(message);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Set up parser and renderer for flexmark.
|
||||||
Parser parser = Parser.builder(options).build();
|
Parser parser = Parser.builder(options).build();
|
||||||
HtmlRenderer renderer = HtmlRenderer.builder(options).build();
|
HtmlRenderer renderer = HtmlRenderer.builder(options).build();
|
||||||
|
|
||||||
String exceptionsToReturn = "";
|
String exceptionsToReturn = "";
|
||||||
String html = "";
|
String html = "";
|
||||||
String toParse = "";
|
String toParse = "";
|
||||||
|
|
||||||
|
//Import markdown from a URL and render it to html.
|
||||||
|
//Enclosed in a try/catch block in order to catch exceptions and return error messages to the user
|
||||||
try {
|
try {
|
||||||
|
//Create a URL object from the URL typed in by the user,
|
||||||
|
// then fetch the URL content line by line and store each line temporarily in inputLine.
|
||||||
|
//Concatenate all the lines into toParse and trim leading and trailing whitespace.
|
||||||
URL importFrom = new URL(bodyContent);
|
URL importFrom = new URL(bodyContent);
|
||||||
BufferedReader in = new BufferedReader(
|
BufferedReader in = new BufferedReader(
|
||||||
new InputStreamReader(importFrom.openStream())
|
new InputStreamReader(importFrom.openStream())
|
||||||
@@ -130,12 +140,17 @@ public class MarkdownFromURLMacro extends BaseMacro implements Macro
|
|||||||
}
|
}
|
||||||
in.close();
|
in.close();
|
||||||
toParse = toParse.trim();
|
toParse = toParse.trim();
|
||||||
|
|
||||||
|
|
||||||
|
//If the content of the URL is a file in a private Bitbucket repository, then throw an exception.
|
||||||
|
//Otherwise, parse and render the markdown.
|
||||||
if (toParse.startsWith("<html>\n<head>\n <title>OpenID transaction in progress</title>")) {
|
if (toParse.startsWith("<html>\n<head>\n <title>OpenID transaction in progress</title>")) {
|
||||||
throw new privateRepositoryException("Cannot import from private repository.");
|
throw new privateRepositoryException("Cannot import from private repository.");
|
||||||
}else {
|
}else {
|
||||||
Node document = parser.parse(toParse);
|
Node document = parser.parse(toParse);
|
||||||
html = renderer.render(document) + highlightjs;
|
html = renderer.render(document) + highlightjs;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
catch (MalformedURLException u) {
|
catch (MalformedURLException u) {
|
||||||
exceptionsToReturn = exceptionsToReturn + "<strong>Error with Markdown From URL macro: Invalid URL.</strong><br>Please enter a valid URL. If you are not trying to import markdown from a URL, use the Markdown macro instead of the Markdown from URL macro.<br>For support <a href='https://community.atlassian.com/t5/tag/addon-com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/tg-p'>visit our Q&A in the Atlassian Community</a>. You can ask a new question by clicking the \"Create\" button on the top right of the Q&A.<br>";
|
exceptionsToReturn = exceptionsToReturn + "<strong>Error with Markdown From URL macro: Invalid URL.</strong><br>Please enter a valid URL. If you are not trying to import markdown from a URL, use the Markdown macro instead of the Markdown from URL macro.<br>For support <a href='https://community.atlassian.com/t5/tag/addon-com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/tg-p'>visit our Q&A in the Atlassian Community</a>. You can ask a new question by clicking the \"Create\" button on the top right of the Q&A.<br>";
|
||||||
@@ -150,9 +165,12 @@ public class MarkdownFromURLMacro extends BaseMacro implements Macro
|
|||||||
exceptionsToReturn = exceptionsToReturn + "<strong>Error with Markdown From URL macro: Unexpected error.</strong><br>" + e.toString() + "<br>For support <a href='https://community.atlassian.com/t5/tag/addon-com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/tg-p'>visit our Q&A in the Atlassian Community</a>. You can ask a new question by clicking the \"Create\" button on the top right of the Q&A.<br>";
|
exceptionsToReturn = exceptionsToReturn + "<strong>Error with Markdown From URL macro: Unexpected error.</strong><br>" + e.toString() + "<br>For support <a href='https://community.atlassian.com/t5/tag/addon-com.atlassian.plugins.confluence.markdown.confluence-markdown-macro/tg-p'>visit our Q&A in the Atlassian Community</a>. You can ask a new question by clicking the \"Create\" button on the top right of the Q&A.<br>";
|
||||||
}
|
}
|
||||||
finally {
|
finally {
|
||||||
|
//If there were exceptions, set the html to return to an error message (or multiple)
|
||||||
if (exceptionsToReturn != "") {
|
if (exceptionsToReturn != "") {
|
||||||
html = "<p style='background: #ffe0e0; border-radius: 5px; padding: 10px;'>" + exceptionsToReturn + "</p>";
|
html = "<p style='background: #ffe0e0; border-radius: 5px; padding: 10px;'>" + exceptionsToReturn + "</p>";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//Return the output, which is either rendered markdown or an error message.
|
||||||
return html;
|
return html;
|
||||||
}
|
}
|
||||||
}else {
|
}else {
|
||||||
|
|||||||
@@ -46,7 +46,6 @@ public class MarkdownMacro extends BaseMacro implements Macro
|
|||||||
{
|
{
|
||||||
|
|
||||||
private final XhtmlContent xhtmlUtils;
|
private final XhtmlContent xhtmlUtils;
|
||||||
|
|
||||||
private PageBuilderService pageBuilderService;
|
private PageBuilderService pageBuilderService;
|
||||||
|
|
||||||
@Autowired
|
@Autowired
|
||||||
@@ -54,12 +53,7 @@ public class MarkdownMacro extends BaseMacro implements Macro
|
|||||||
this.pageBuilderService = pageBuilderService;
|
this.pageBuilderService = pageBuilderService;
|
||||||
this.xhtmlUtils = xhtmlUtils;
|
this.xhtmlUtils = xhtmlUtils;
|
||||||
}
|
}
|
||||||
|
|
||||||
// public MarkdownMacro(XhtmlContent xhtmlUtils)
|
|
||||||
// {
|
|
||||||
// this.xhtmlUtils = xhtmlUtils;
|
|
||||||
// }
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public BodyType getBodyType()
|
public BodyType getBodyType()
|
||||||
{
|
{
|
||||||
@@ -76,9 +70,11 @@ public class MarkdownMacro extends BaseMacro implements Macro
|
|||||||
public String execute(Map<String, String> parameters, String bodyContent, ConversionContext conversionContext) throws MacroExecutionException
|
public String execute(Map<String, String> parameters, String bodyContent, ConversionContext conversionContext) throws MacroExecutionException
|
||||||
{
|
{
|
||||||
|
|
||||||
|
//Include highlight.js in the webpage.
|
||||||
|
//Adds something like <script type="text/javascript" src="path-to-javascript-file.js"></script> to the <head> of the page
|
||||||
pageBuilderService.assembler().resources().requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs");
|
pageBuilderService.assembler().resources().requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs");
|
||||||
|
|
||||||
|
//Set options for flexmark. See flexmark documentation for more info.
|
||||||
MutableDataSet options = new MutableDataSet();
|
MutableDataSet options = new MutableDataSet();
|
||||||
|
|
||||||
options.set(Parser.EXTENSIONS, Arrays.asList(
|
options.set(Parser.EXTENSIONS, Arrays.asList(
|
||||||
@@ -95,21 +91,24 @@ public class MarkdownMacro extends BaseMacro implements Macro
|
|||||||
YouTubeLinkExtension.create()
|
YouTubeLinkExtension.create()
|
||||||
|
|
||||||
));
|
));
|
||||||
|
|
||||||
|
|
||||||
|
//JavaScript for syntax highlighting. See highlight.js documentation for more info.
|
||||||
String highlightjs = "<script>\n" +
|
String highlightjs = "<script>\n" +
|
||||||
"AJS.$('[data-macro-name=\"markdown\"] code').each(function(i, block) {\n" +
|
"AJS.$('[data-macro-name=\"markdown\"] code').each(function(i, block) {\n" +
|
||||||
" hljs.highlightBlock(block);\n" +
|
" hljs.highlightBlock(block);\n" +
|
||||||
" });\n" +
|
" });\n" +
|
||||||
"</script>";
|
"</script>";
|
||||||
|
|
||||||
|
//Set up parser and renderer for flexmark.
|
||||||
Parser parser = Parser.builder(options).build();
|
Parser parser = Parser.builder(options).build();
|
||||||
HtmlRenderer renderer = HtmlRenderer.builder(options).build();
|
HtmlRenderer renderer = HtmlRenderer.builder(options).build();
|
||||||
|
|
||||||
|
// Parse and render the body content from markdown to HTML,
|
||||||
|
// then return the output along with the JavaScript for syntax highlighting
|
||||||
Node document = parser.parse(bodyContent);
|
Node document = parser.parse(bodyContent);
|
||||||
String html = renderer.render(document ) + highlightjs; // "<p>This is <em>Sparta</em></p>\n"
|
String html = renderer.render(document) + highlightjs;
|
||||||
return html;
|
return html;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
@@ -1,6 +0,0 @@
|
|||||||
package com.atlassian.plugins.confluence.markdown;
|
|
||||||
|
|
||||||
public interface MyPluginComponent
|
|
||||||
{
|
|
||||||
String getName();
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,172 @@
|
|||||||
|
package ut.com.atlassian.plugins.confluence;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
|
|
||||||
|
import org.mockito.*;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.FileWriter;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.net.*;
|
||||||
|
|
||||||
|
import com.atlassian.confluence.content.render.xhtml.ConversionContext;
|
||||||
|
import com.atlassian.confluence.macro.MacroExecutionException;
|
||||||
|
import com.atlassian.webresource.api.assembler.PageBuilderService;
|
||||||
|
import com.atlassian.webresource.api.assembler.RequiredResources;
|
||||||
|
import com.atlassian.webresource.api.assembler.WebResourceAssembler;
|
||||||
|
import com.gargoylesoftware.htmlunit.FailingHttpStatusCodeException;
|
||||||
|
import com.gargoylesoftware.htmlunit.WebClient;
|
||||||
|
import com.gargoylesoftware.htmlunit.html.HtmlElement;
|
||||||
|
import com.gargoylesoftware.htmlunit.html.HtmlPage;
|
||||||
|
import com.atlassian.plugins.confluence.markdown.MarkdownFromURLMacro;
|
||||||
|
|
||||||
|
@RunWith (MockitoJUnitRunner.class)
|
||||||
|
public class MarkdownFromURLUnitTest {
|
||||||
|
@Mock
|
||||||
|
ConversionContext conversionContext;
|
||||||
|
@Mock
|
||||||
|
PageBuilderService pageBuilderService;
|
||||||
|
@Mock
|
||||||
|
WebResourceAssembler webResourceAssembler;
|
||||||
|
@Mock
|
||||||
|
RequiredResources requiredResources;
|
||||||
|
@InjectMocks
|
||||||
|
MarkdownFromURLMacro markdownMacro;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMarkdownRendering() throws MacroExecutionException, MalformedURLException {
|
||||||
|
/*Test that markdown is correctly retrieved from a URL and rendered into HTML*/
|
||||||
|
String file = new File("src/test/resources/testMarkdown.md").toURI().toURL().toString();
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
String output = markdownMacro.execute(new HashMap(), file, conversionContext);
|
||||||
|
assertTrue(output.contains("<em>Italic</em>"));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testErrorHandling() throws MacroExecutionException, MalformedURLException {
|
||||||
|
/*Test error handling of nonexistent URLs*/
|
||||||
|
String input1 = new File("src/test/resources/nonexistentfile.md").toURI().toURL().toString();
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
String output1 = markdownMacro.execute(new HashMap(), input1, conversionContext);
|
||||||
|
assertTrue(output1.contains("URL does not exist"));
|
||||||
|
assertTrue(output1.contains(input1));
|
||||||
|
|
||||||
|
|
||||||
|
/*Test error handling of invalid URLs*/
|
||||||
|
String input2 = "not_a_URL";
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
String output2 = markdownMacro.execute(new HashMap(), input2, conversionContext);
|
||||||
|
assertTrue(output2.contains("Invalid URL"));
|
||||||
|
|
||||||
|
|
||||||
|
/*Test error handling of importing from a private Bitbucket repository*/
|
||||||
|
String input3 = new File("src/test/resources/testPrivateBitbucket.html").toURI().toURL().toString();
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
String output3 = markdownMacro.execute(new HashMap(), input3, conversionContext);
|
||||||
|
assertTrue(output3.contains("Importing from private Bitbucket repositories is not supported"));
|
||||||
|
assertTrue(output3.contains(input3));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testSyntaxHighlighting() throws MacroExecutionException, FailingHttpStatusCodeException, MalformedURLException, IOException {
|
||||||
|
/*Test that the javascript for syntax highlighting works*/
|
||||||
|
// Run the macro with an input of a line of code
|
||||||
|
// Create a temporary HTML file containing the output
|
||||||
|
// Parse the HTML file with htmlunit
|
||||||
|
// Assert that the page contains three spans with the correct classes
|
||||||
|
// Note: Does not test if highlight.js and highlight.css are correctly included in the page
|
||||||
|
try (final WebClient webClient = new WebClient()) {
|
||||||
|
String file = new File("src/test/resources/testSyntaxHighlighting.md").toURI().toURL().toString();
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
String output = markdownMacro.execute(new HashMap(), file, conversionContext);
|
||||||
|
String toWrite = "<!DOCTYPE html>\r\n" +
|
||||||
|
"<html>\r\n" +
|
||||||
|
"<head>\r\n" +
|
||||||
|
" <title>Syntax Highlighting Page</title>\r\n" +
|
||||||
|
" <script src=\"./jquery-3.3.1.min.js\"></script>\r\n" +
|
||||||
|
" <script src=\"../../main/resources/js/highlight.min.js\"></script>\r\n" +
|
||||||
|
" <link href=\"../../main/resources/css/highlight.min.css\" rel=\"stylesheet\" />\r\n" +
|
||||||
|
"</head>\r\n" +
|
||||||
|
"<body>\r\n" +
|
||||||
|
" <script>\r\n" +
|
||||||
|
" var AJS = {\r\n" +
|
||||||
|
" $: $\r\n" +
|
||||||
|
" };\r\n" +
|
||||||
|
" </script>\r\n" +
|
||||||
|
"<div data-macro-name='markdown'>" +
|
||||||
|
output +
|
||||||
|
"</div>" +
|
||||||
|
"</body>\r\n" +
|
||||||
|
"</html>";
|
||||||
|
File tmpHTMLFile = File.createTempFile("syntax-highlighting-", ".html", new File("src/test/resources"));
|
||||||
|
FileWriter writer = new FileWriter(tmpHTMLFile);
|
||||||
|
writer.write(toWrite);
|
||||||
|
writer.close();
|
||||||
|
|
||||||
|
final HtmlPage page = webClient.getPage(tmpHTMLFile.toURI().toURL().toString());
|
||||||
|
HtmlElement document = page.getDocumentElement();
|
||||||
|
List<HtmlElement> spans = document.getElementsByTagName("span");
|
||||||
|
tmpHTMLFile.delete();
|
||||||
|
|
||||||
|
HtmlElement hljsClassSpan = null;
|
||||||
|
int numberOfHljsClassSpans = 0;
|
||||||
|
HtmlElement hljsKeywordSpan = null;
|
||||||
|
int numberOfHljsKeywordSpans = 0;
|
||||||
|
HtmlElement hljsTitleSpan = null;
|
||||||
|
int numberOfHljsTitleSpans = 0;
|
||||||
|
|
||||||
|
//Check the class of every span element and increment the counters accordingly
|
||||||
|
//Also define the three span objects as the first span of the correct class
|
||||||
|
for (HtmlElement span : spans) {
|
||||||
|
if (span.getAttribute("class").contains("hljs-class")) {
|
||||||
|
numberOfHljsClassSpans++;
|
||||||
|
if (numberOfHljsClassSpans == 1) {
|
||||||
|
hljsClassSpan = span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (span.getAttribute("class").contains("hljs-keyword")) {
|
||||||
|
numberOfHljsKeywordSpans++;
|
||||||
|
if (numberOfHljsKeywordSpans == 1) {
|
||||||
|
hljsKeywordSpan = span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (span.getAttribute("class").contains("hljs-title")) {
|
||||||
|
numberOfHljsTitleSpans++;
|
||||||
|
if (numberOfHljsTitleSpans == 1) {
|
||||||
|
hljsTitleSpan = span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that there is exactly one span with a css class of hljs-class
|
||||||
|
// and that it's parent is a code element with a css class of hljs
|
||||||
|
assertThat(numberOfHljsClassSpans, is(1));
|
||||||
|
assertTrue(hljsClassSpan.getParentNode().getNodeName().equals("code"));
|
||||||
|
assertTrue(hljsClassSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs"));
|
||||||
|
|
||||||
|
// Test that there is exactly one span with a css class of hljs-keyword
|
||||||
|
// and that it's parent is a span element with a css class of hljs-class
|
||||||
|
assertThat(numberOfHljsKeywordSpans, is(1));
|
||||||
|
assertTrue(hljsKeywordSpan.getParentNode().getNodeName().equals("span"));
|
||||||
|
assertTrue(hljsKeywordSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs-class"));
|
||||||
|
|
||||||
|
// Test that there is exactly one span with a css class of hljs-title
|
||||||
|
// and that it's parent is a span element with a css class of hljs-class
|
||||||
|
assertThat(numberOfHljsTitleSpans, is(1));
|
||||||
|
assertTrue(hljsTitleSpan.getParentNode().getNodeName().equals("span"));
|
||||||
|
assertTrue(hljsTitleSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs-class"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
//Mock methods for pageBuilderService.assembler().resources().requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs");
|
||||||
|
Mockito.when(pageBuilderService.assembler()).thenReturn(webResourceAssembler);
|
||||||
|
Mockito.when(webResourceAssembler.resources()).thenReturn(requiredResources);
|
||||||
|
Mockito.when(requiredResources.requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs")).thenReturn(requiredResources);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,144 @@
|
|||||||
|
package ut.com.atlassian.plugins.confluence;
|
||||||
|
|
||||||
|
import org.junit.Before;
|
||||||
|
import org.junit.Test;
|
||||||
|
import org.junit.runner.RunWith;
|
||||||
|
import static org.junit.Assert.*;
|
||||||
|
import static org.hamcrest.CoreMatchers.*;
|
||||||
|
|
||||||
|
import com.gargoylesoftware.htmlunit.*;
|
||||||
|
import com.gargoylesoftware.htmlunit.html.*;
|
||||||
|
|
||||||
|
import org.mockito.*;
|
||||||
|
import org.mockito.runners.MockitoJUnitRunner;
|
||||||
|
|
||||||
|
import java.io.*;
|
||||||
|
import java.net.MalformedURLException;
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.atlassian.confluence.content.render.xhtml.ConversionContext;
|
||||||
|
import com.atlassian.confluence.macro.MacroExecutionException;
|
||||||
|
import com.atlassian.webresource.api.assembler.PageBuilderService;
|
||||||
|
import com.atlassian.webresource.api.assembler.RequiredResources;
|
||||||
|
import com.atlassian.webresource.api.assembler.WebResourceAssembler;
|
||||||
|
import com.atlassian.plugins.confluence.markdown.MarkdownMacro;
|
||||||
|
|
||||||
|
@RunWith (MockitoJUnitRunner.class)
|
||||||
|
public class MarkdownUnitTest {
|
||||||
|
@Mock
|
||||||
|
ConversionContext conversionContext;
|
||||||
|
@Mock
|
||||||
|
PageBuilderService pageBuilderService;
|
||||||
|
@Mock
|
||||||
|
WebResourceAssembler webResourceAssembler;
|
||||||
|
@Mock
|
||||||
|
RequiredResources requiredResources;
|
||||||
|
@InjectMocks
|
||||||
|
MarkdownMacro markdownMacro;
|
||||||
|
|
||||||
|
@Test
|
||||||
|
public void testMarkdownRendering() throws MacroExecutionException {
|
||||||
|
/*Test that markdown is correctly rendered into HTML*/
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
String output = markdownMacro.execute(new HashMap(), "*Italic*", conversionContext);
|
||||||
|
assertTrue(output.contains("<em>Italic</em>"));
|
||||||
|
}
|
||||||
|
@Test
|
||||||
|
public void testSyntaxHighlighting() throws MacroExecutionException, IOException {
|
||||||
|
/*Test that the javascript for syntax highlighting works*/
|
||||||
|
// Run the macro with an input of a line of code
|
||||||
|
// Create a temporary HTML file containing the output
|
||||||
|
// Parse the HTML file with htmlunit
|
||||||
|
// Assert that the page contains three spans with the correct classes
|
||||||
|
// Note: Does not test if highlight.js and highlight.css are correctly included in the page
|
||||||
|
try (final WebClient webClient = new WebClient()) {
|
||||||
|
@SuppressWarnings({ "rawtypes", "unchecked" })
|
||||||
|
String output = markdownMacro.execute(new HashMap(), "`class className() {}`", conversionContext);
|
||||||
|
String toWrite = "<!DOCTYPE html>\r\n" +
|
||||||
|
"<html>\r\n" +
|
||||||
|
"<head>\r\n" +
|
||||||
|
" <title>Syntax Highlighting Page</title>\r\n" +
|
||||||
|
" <script src=\"./jquery-3.3.1.min.js\"></script>\r\n" +
|
||||||
|
" <script src=\"../../main/resources/js/highlight.min.js\"></script>\r\n" +
|
||||||
|
" <link href=\"../../main/resources/css/highlight.min.css\" rel=\"stylesheet\" />\r\n" +
|
||||||
|
"</head>\r\n" +
|
||||||
|
"<body>\r\n" +
|
||||||
|
" <script>\r\n" +
|
||||||
|
" var AJS = {\r\n" +
|
||||||
|
" $: $\r\n" +
|
||||||
|
" };\r\n" +
|
||||||
|
" </script>\r\n" +
|
||||||
|
"<div data-macro-name='markdown'>" +
|
||||||
|
output +
|
||||||
|
"</div>" +
|
||||||
|
"</body>\r\n" +
|
||||||
|
"</html>";
|
||||||
|
File tmpHTMLFile = File.createTempFile("syntax-highlighting-", ".html", new File("src/test/resources"));
|
||||||
|
FileWriter writer = new FileWriter(tmpHTMLFile);
|
||||||
|
writer.write(toWrite);
|
||||||
|
writer.close();
|
||||||
|
|
||||||
|
final HtmlPage page = webClient.getPage(tmpHTMLFile.toURI().toURL().toString());
|
||||||
|
HtmlElement document = page.getDocumentElement();
|
||||||
|
List<HtmlElement> spans = document.getElementsByTagName("span");
|
||||||
|
tmpHTMLFile.delete();
|
||||||
|
|
||||||
|
HtmlElement hljsClassSpan = null;
|
||||||
|
int numberOfHljsClassSpans = 0;
|
||||||
|
HtmlElement hljsKeywordSpan = null;
|
||||||
|
int numberOfHljsKeywordSpans = 0;
|
||||||
|
HtmlElement hljsTitleSpan = null;
|
||||||
|
int numberOfHljsTitleSpans = 0;
|
||||||
|
|
||||||
|
//Check the class of every span element and increment the counters accordingly
|
||||||
|
//Also define the three span objects as the first span of the correct class
|
||||||
|
for (HtmlElement span : spans) {
|
||||||
|
if (span.getAttribute("class").contains("hljs-class")) {
|
||||||
|
numberOfHljsClassSpans++;
|
||||||
|
if (numberOfHljsClassSpans == 1) {
|
||||||
|
hljsClassSpan = span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (span.getAttribute("class").contains("hljs-keyword")) {
|
||||||
|
numberOfHljsKeywordSpans++;
|
||||||
|
if (numberOfHljsKeywordSpans == 1) {
|
||||||
|
hljsKeywordSpan = span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (span.getAttribute("class").contains("hljs-title")) {
|
||||||
|
numberOfHljsTitleSpans++;
|
||||||
|
if (numberOfHljsTitleSpans == 1) {
|
||||||
|
hljsTitleSpan = span;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test that there is exactly one span with a css class of hljs-class
|
||||||
|
// and that it's parent is a code element with a css class of hljs
|
||||||
|
assertThat(numberOfHljsClassSpans, is(1));
|
||||||
|
assertTrue(hljsClassSpan.getParentNode().getNodeName().equals("code"));
|
||||||
|
assertTrue(hljsClassSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs"));
|
||||||
|
|
||||||
|
// Test that there is exactly one span with a css class of hljs-keyword
|
||||||
|
// and that it's parent is a span element with a css class of hljs-class
|
||||||
|
assertThat(numberOfHljsKeywordSpans, is(1));
|
||||||
|
assertTrue(hljsKeywordSpan.getParentNode().getNodeName().equals("span"));
|
||||||
|
assertTrue(hljsKeywordSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs-class"));
|
||||||
|
|
||||||
|
// Test that there is exactly one span with a css class of hljs-title
|
||||||
|
// and that it's parent is a span element with a css class of hljs-class
|
||||||
|
assertThat(numberOfHljsTitleSpans, is(1));
|
||||||
|
assertTrue(hljsTitleSpan.getParentNode().getNodeName().equals("span"));
|
||||||
|
assertTrue(hljsTitleSpan.getParentNode().getAttributes().getNamedItem("class").getNodeValue().contains("hljs-class"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Before
|
||||||
|
public void setup() {
|
||||||
|
//Mock methods for pageBuilderService.assembler().resources().requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs");
|
||||||
|
Mockito.when(pageBuilderService.assembler()).thenReturn(webResourceAssembler);
|
||||||
|
Mockito.when(webResourceAssembler.resources()).thenReturn(requiredResources);
|
||||||
|
Mockito.when(requiredResources.requireWebResource("com.atlassian.plugins.confluence.markdown.confluence-markdown-macro:highlightjs")).thenReturn(requiredResources);
|
||||||
|
}
|
||||||
|
}
|
||||||
2
src/test/resources/jquery-3.3.1.min.js
vendored
Normal file
2
src/test/resources/jquery-3.3.1.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
1
src/test/resources/testMarkdown.md
Normal file
1
src/test/resources/testMarkdown.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
*Italic*
|
||||||
15
src/test/resources/testPrivateBitbucket.html
Normal file
15
src/test/resources/testPrivateBitbucket.html
Normal file
@@ -0,0 +1,15 @@
|
|||||||
|
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<title>OpenID transaction in progress</title>
|
||||||
|
</head>
|
||||||
|
<body onload="document.forms[0].submit();">
|
||||||
|
<form id="openid_message" action="https://id.atlassian.com/openid/v2/op" method="post" accept-charset="UTF-8" enctype="application/x-www-form-urlencoded"><input name="openid.ns.crowdid" type="hidden" value="https://developer.atlassian.com/display/CROWDDEV/CrowdID+OpenID+extensions#CrowdIDOpenIDextensions-login-page-parameters"/><input name="openid.return_to" type="hidden" value="https://bitbucket.org/socialauth/complete/atlassianid/?janrain_nonce=2018-09-03T16%3A44%3A09ZAMqmtD"/><input name="openid.realm" type="hidden" value="https://bitbucket.org"/><input name="openid.ns" type="hidden" value="http://specs.openid.net/auth/2.0"/><input name="openid.sreg.optional" type="hidden" value="fullname,nickname,email"/><input name="openid.claimed_id" type="hidden" value="http://specs.openid.net/auth/2.0/identifier_select"/><input name="openid.ns.sreg" type="hidden" value="http://openid.net/extensions/sreg/1.1"/><input name="openid.crowdid.application" type="hidden" value="bitbucket"/><input name="openid.assoc_handle" type="hidden" value="8063999"/><input name="openid.mode" type="hidden" value="checkid_setup"/><input name="openid.identity" type="hidden" value="http://specs.openid.net/auth/2.0/identifier_select"/><input type="submit" value="Continue"/></form>
|
||||||
|
<script>
|
||||||
|
var elements = document.forms[0].elements;
|
||||||
|
for (var i = 0; i < elements.length; i++) {
|
||||||
|
elements[i].style.display = "none";
|
||||||
|
}
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
||||||
1
src/test/resources/testSyntaxHighlighting.md
Normal file
1
src/test/resources/testSyntaxHighlighting.md
Normal file
@@ -0,0 +1 @@
|
|||||||
|
`class className() {}`
|
||||||
Reference in New Issue
Block a user