diff --git a/src/main/java/com/atlassian/plugins/confluence/markdown/MarkdownFromURLMacro.java b/src/main/java/com/atlassian/plugins/confluence/markdown/MarkdownFromURLMacro.java index 5ebcaac..26c3f12 100644 --- a/src/main/java/com/atlassian/plugins/confluence/markdown/MarkdownFromURLMacro.java +++ b/src/main/java/com/atlassian/plugins/confluence/markdown/MarkdownFromURLMacro.java @@ -77,10 +77,13 @@ public class MarkdownFromURLMacro extends BaseMacro implements Macro @Override public String execute(Map 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) { + //Include highlight.js in the webpage. + //Adds something like to the of the page 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(); options.set(Parser.EXTENSIONS, Arrays.asList( @@ -98,26 +101,35 @@ public class MarkdownFromURLMacro extends BaseMacro implements Macro )); - + + //JavaScript for syntax highlighting. See highlight.js documentation for more info. String highlightjs = ""; + //Define a custom exception for trying to import from a private Bitbucket repository class privateRepositoryException extends Exception { public privateRepositoryException(String message) { super(message); } } + //Set up parser and renderer for flexmark. Parser parser = Parser.builder(options).build(); HtmlRenderer renderer = HtmlRenderer.builder(options).build(); String exceptionsToReturn = ""; String html = ""; 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 { + //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); BufferedReader in = new BufferedReader( new InputStreamReader(importFrom.openStream()) @@ -128,12 +140,17 @@ public class MarkdownFromURLMacro extends BaseMacro implements Macro } in.close(); 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("\n\n OpenID transaction in progress")) { throw new privateRepositoryException("Cannot import from private repository."); }else { Node document = parser.parse(toParse); html = renderer.render(document) + highlightjs; } + } catch (MalformedURLException u) { exceptionsToReturn = exceptionsToReturn + "Error with Markdown From URL macro: Invalid URL.
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.
For support visit our Q&A in the Atlassian Community. You can ask a new question by clicking the \"Create\" button on the top right of the Q&A.
"; @@ -148,9 +165,12 @@ public class MarkdownFromURLMacro extends BaseMacro implements Macro exceptionsToReturn = exceptionsToReturn + "Error with Markdown From URL macro: Unexpected error.
" + e.toString() + "
For support visit our Q&A in the Atlassian Community. You can ask a new question by clicking the \"Create\" button on the top right of the Q&A.
"; } finally { + //If there were exceptions, set the html to return to an error message (or multiple) if (exceptionsToReturn != "") { html = "

" + exceptionsToReturn + "

"; } + + //Return the output, which is either rendered markdown or an error message. return html; } }else { diff --git a/src/main/java/com/atlassian/plugins/confluence/markdown/MarkdownMacro.java b/src/main/java/com/atlassian/plugins/confluence/markdown/MarkdownMacro.java index febab88..8aa17fa 100644 --- a/src/main/java/com/atlassian/plugins/confluence/markdown/MarkdownMacro.java +++ b/src/main/java/com/atlassian/plugins/confluence/markdown/MarkdownMacro.java @@ -70,9 +70,11 @@ public class MarkdownMacro extends BaseMacro implements Macro public String execute(Map parameters, String bodyContent, ConversionContext conversionContext) throws MacroExecutionException { - + //Include highlight.js in the webpage. + //Adds something like to the of the page 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(); options.set(Parser.EXTENSIONS, Arrays.asList( @@ -89,16 +91,21 @@ public class MarkdownMacro extends BaseMacro implements Macro YouTubeLinkExtension.create() )); - - + + + //JavaScript for syntax highlighting. See highlight.js documentation for more info. String highlightjs = ""; + + //Set up parser and renderer for flexmark. Parser parser = Parser.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); String html = renderer.render(document) + highlightjs; return html; diff --git a/src/test/java/ut/com/atlassian/plugins/confluence/MarkdownFromURLUnitTest.java b/src/test/java/ut/com/atlassian/plugins/confluence/MarkdownFromURLUnitTest.java index 83e893d..aff79b5 100644 --- a/src/test/java/ut/com/atlassian/plugins/confluence/MarkdownFromURLUnitTest.java +++ b/src/test/java/ut/com/atlassian/plugins/confluence/MarkdownFromURLUnitTest.java @@ -37,27 +37,51 @@ public class MarkdownFromURLUnitTest { @Test public void testMarkdownRendering() throws MacroExecutionException, MalformedURLException { - Mockito.when(pageBuilderService.assembler()).thenReturn(webResourceAssembler); + //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); + + /*Test that markdown is correctly retrieved from a URL and rendered into HTML*/ + // Run the macro using a URL that points to a file containing test markdown, + // then assert that *Italic* was correctly rendered into Italic String file = new File("src/test/resources/testMarkdown.md").toURI().toURL().toString(); @SuppressWarnings({ "rawtypes", "unchecked" }) String output = markdownMacro.execute(new HashMap(), file, conversionContext); - assertThat(Pattern.matches("[\\S\\s]*Italic[\\S\\s]*", output), is(true)); + assertThat(Pattern.matches("[\\S\\s]*Italic[\\S\\s]*", output), is(true)); //Uses [\S\s] (anything that is either whitespace or not whitespace) instead of . (any character) because . does not match newline characters. } @Test public void testErrorHandling() throws MacroExecutionException, MalformedURLException { - Mockito.when(pageBuilderService.assembler()).thenReturn(webResourceAssembler); + //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); - String input1 = new File("src/test/resources/nonexistantfile.md").toURI().toURL().toString(); + + /*Test error handling of nonexistent URLs*/ + // Run the macro using a URL pointing to a file that does not exist, + // then assert that the output of the macro is not an empty string + // i.e. that it returned an error message instead of simply breaking. + String input1 = new File("src/test/resources/nonexistentfile.md").toURI().toURL().toString(); @SuppressWarnings({ "rawtypes", "unchecked" }) String output1 = markdownMacro.execute(new HashMap(), input1, conversionContext); assertThat(output1, is(not(""))); + + + /*Test error handling of invalid URLs*/ + // Run the macro using a string that is not a URL, + // then assert that the output of the macro is not an empty string + // i.e. that it returned an error message instead of simply breaking. String input2 = "not_a_URL"; @SuppressWarnings({ "rawtypes", "unchecked" }) String output2 = markdownMacro.execute(new HashMap(), input2, conversionContext); assertThat(output2, is(not(""))); + + + /*Test error handling of importing from a private Bitbucket repository*/ + // Run the macro using a URL pointing to a file that mimics the text + // returned when trying to import from a private Bitbucket repository, + // then assert that the output of the macro is not an empty string + // i.e. that it returned an error message instead of simply breaking. String input3 = new File("src/test/resources/testPrivateBitbucket.html").toURI().toURL().toString(); @SuppressWarnings({ "rawtypes", "unchecked" }) String output3 = markdownMacro.execute(new HashMap(), input3, conversionContext); diff --git a/src/test/java/ut/com/atlassian/plugins/confluence/MarkdownUnitTest.java b/src/test/java/ut/com/atlassian/plugins/confluence/MarkdownUnitTest.java index 0e49a98..81bfad7 100644 --- a/src/test/java/ut/com/atlassian/plugins/confluence/MarkdownUnitTest.java +++ b/src/test/java/ut/com/atlassian/plugins/confluence/MarkdownUnitTest.java @@ -33,21 +33,34 @@ public class MarkdownUnitTest { @Test public void testMarkdownRendering() throws MacroExecutionException { + //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); + + + /*Test that markdown is correctly rendered into HTML*/ + // Run the macro using input text of *Italic*, + // then assert that *Italic* was correctly rendered into Italic @SuppressWarnings({ "rawtypes", "unchecked" }) String output = markdownMacro.execute(new HashMap(), "*Italic*", conversionContext); - assertTrue(Pattern.matches("[\\S\\s]*Italic[\\S\\s]*", output)); + assertTrue(Pattern.matches("[\\S\\s]*Italic[\\S\\s]*", output)); //Uses [\S\s] (anything that is either whitespace or not whitespace) instead of . (any character) because . does not match newline characters. } @Test public void testSyntaxHighlighting() throws MacroExecutionException { + //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); + + + /*Test that the correct JavaScript is returned for highlight.js to work*/ + // Run the macro using input of a line of code in a code block, + // then assert that the correct JavaScript was returned. + // Intended only as a temporary test until I can program a better one @SuppressWarnings({ "rawtypes", "unchecked" }) - String output = markdownMacro.execute(new HashMap(), "public class JavaClass {}", conversionContext); + String output = markdownMacro.execute(new HashMap(), "'public class JavaClass {}'", conversionContext); assertTrue(Pattern.matches("[\\S\\s]*