Skip to content
Snippets Groups Projects
Commit 3a425387 authored by Marius Dumitru Florea's avatar Marius Dumitru Florea
Browse files

XWIKI-19270: Add support for performing the PDF export using a browser running...

XWIKI-19270: Add support for performing the PDF export using a browser running in a Docker container
* Add unit tests

(cherry picked from commit 0a0087ef)
parent 3f0a3ec5
No related branches found
No related tags found
No related merge requests found
......@@ -34,6 +34,7 @@
<properties>
<!-- Name to display by the Extension Manager -->
<xwiki.extension.name>PDF Export API</xwiki.extension.name>
<xwiki.jacoco.instructionRatio>0.69</xwiki.jacoco.instructionRatio>
</properties>
<dependencies>
<dependency>
......@@ -83,5 +84,11 @@
<version>${commons.version}</version>
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.xwiki.rendering</groupId>
<artifactId>xwiki-rendering-syntax-event</artifactId>
<version>${rendering.version}</version>
<scope>test</scope>
</dependency>
</dependencies>
</project>
......@@ -116,6 +116,10 @@ public void initialize() throws InitializationException
public List<Block> execute(PDFTocMacroParameters parameters, String content, MacroTransformationContext context)
throws MacroExecutionException
{
if (parameters.getJobId() == null) {
throw new MacroExecutionException("The mandatory job id parameter is missing.");
}
PDFExportJobStatus jobStatus = getJobStatus(parameters.getJobId());
if (jobStatus != null && Objects.equals(jobStatus.getRequest().getUserReference(),
this.documentAccessBridge.getCurrentUserReference())) {
......
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.export.pdf.internal.job;
import java.util.Arrays;
import javax.inject.Named;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.mockito.invocation.InvocationOnMock;
import org.mockito.stubbing.Answer;
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.bridge.DocumentModelBridge;
import org.xwiki.component.manager.ComponentManager;
import org.xwiki.display.internal.DocumentDisplayer;
import org.xwiki.display.internal.DocumentDisplayerParameters;
import org.xwiki.export.pdf.job.PDFExportJobStatus.DocumentRenderingResult;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.block.WordBlock;
import org.xwiki.rendering.block.XDOM;
import org.xwiki.rendering.renderer.BlockRenderer;
import org.xwiki.rendering.renderer.printer.WikiPrinter;
import org.xwiki.rendering.syntax.Syntax;
import org.xwiki.rendering.transformation.RenderingContext;
import org.xwiki.test.junit5.mockito.ComponentTest;
import org.xwiki.test.junit5.mockito.InjectMockComponents;
import org.xwiki.test.junit5.mockito.MockComponent;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.same;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.when;
/**
* Unit tests for {@link DocumentRenderer}.
*
* @version $Id$
*/
@ComponentTest
class DocumentRendererTest
{
@InjectMockComponents
private DocumentRenderer documentRenderer;
@MockComponent
private DocumentAccessBridge documentAccessBridge;
@MockComponent
private RenderingContext renderingContext;
@MockComponent
@Named("configured")
private DocumentDisplayer documentDisplayer;
@MockComponent
@Named("context")
private ComponentManager contextComponentManager;
@Mock
private BlockRenderer html5Renderer;
@Mock
private DocumentModelBridge document;
@BeforeEach
void configure() throws Exception
{
when(this.renderingContext.getTargetSyntax()).thenReturn(Syntax.HTML_5_0);
when(this.contextComponentManager.getInstance(BlockRenderer.class, Syntax.HTML_5_0.toIdString()))
.thenReturn(this.html5Renderer);
}
@Test
void render() throws Exception
{
DocumentReference documentReference = new DocumentReference("test", "Some", "Page");
when(this.documentAccessBridge.getTranslatedDocumentInstance(documentReference)).thenReturn(this.document);
XDOM xdom = new XDOM(Arrays.asList(new WordBlock("test")));
when(this.documentDisplayer.display(same(this.document), any(DocumentDisplayerParameters.class)))
.thenReturn(xdom);
doAnswer(new Answer<Void>()
{
public Void answer(InvocationOnMock invocation)
{
WikiPrinter printer = (WikiPrinter) invocation.getArguments()[1];
printer.print("some content");
return null;
}
}).when(this.html5Renderer).render(same(xdom), any(WikiPrinter.class));
DocumentRenderingResult result = this.documentRenderer.render(documentReference);
assertEquals(documentReference, result.getDocumentReference());
assertSame(xdom, result.getXDOM());
assertEquals("some content", result.getHTML());
}
}
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.export.pdf.internal.job;
import java.io.InputStream;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.inject.Named;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.xwiki.export.pdf.PDFPrinter;
import org.xwiki.export.pdf.internal.RequiredSkinExtensionsRecorder;
import org.xwiki.export.pdf.job.PDFExportJobRequest;
import org.xwiki.export.pdf.job.PDFExportJobStatus;
import org.xwiki.export.pdf.job.PDFExportJobStatus.DocumentRenderingResult;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.block.WordBlock;
import org.xwiki.rendering.block.XDOM;
import org.xwiki.resource.temporary.TemporaryResourceReference;
import org.xwiki.resource.temporary.TemporaryResourceStore;
import org.xwiki.security.authorization.AuthorizationManager;
import org.xwiki.security.authorization.Right;
import org.xwiki.test.junit5.mockito.ComponentTest;
import org.xwiki.test.junit5.mockito.InjectMockComponents;
import org.xwiki.test.junit5.mockito.MockComponent;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertNull;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Unit tests for {@link PDFExportJob}.
*
* @version $Id$
*/
@ComponentTest
class PDFExportJobTest
{
@InjectMockComponents
private PDFExportJob pdfExportJob;
@MockComponent
private AuthorizationManager authorization;
@MockComponent
private DocumentRenderer documentRenderer;
@MockComponent
private RequiredSkinExtensionsRecorder requiredSkinExtensionsRecorder;
@MockComponent
@Named("docker")
private PDFPrinter<PDFExportJobRequest> pdfPrinter;
@MockComponent
private TemporaryResourceStore temporaryResourceStore;
private DocumentReference firstPageReference = new DocumentReference("test", "First", "Page");
private DocumentRenderingResult firstPageRendering = new DocumentRenderingResult(this.firstPageReference,
new XDOM(Collections.singletonList(new WordBlock("first"))), "first HTML");
private DocumentReference secondPageReference = new DocumentReference("test", "Second", "Page");
private DocumentRenderingResult secondPageRendering = new DocumentRenderingResult(this.secondPageReference,
new XDOM(Collections.singletonList(new WordBlock("second"))), "second HTML");
private PDFExportJobRequest request = new PDFExportJobRequest();
private DocumentReference aliceReference = new DocumentReference("test", "Users", "Alice");
private DocumentReference bobReference = new DocumentReference("test", "Users", "Bob");
@BeforeEach
void configure() throws Exception
{
DocumentReference thirdPageReference = new DocumentReference("test", "Third", "Page");
when(this.authorization.hasAccess(Right.VIEW, this.aliceReference, thirdPageReference)).thenReturn(true);
DocumentReference fourthPageReference = new DocumentReference("test", "Fourth", "Page");
when(this.authorization.hasAccess(Right.VIEW, this.bobReference, fourthPageReference)).thenReturn(true);
this.request.setCheckRights(true);
this.request.setCheckAuthorRights(true);
this.request.setUserReference(this.aliceReference);
this.request.setAuthorReference(this.bobReference);
this.request.setDocuments(
Arrays.asList(this.firstPageReference, this.secondPageReference, thirdPageReference, fourthPageReference));
when(this.authorization.hasAccess(Right.VIEW, this.aliceReference, this.firstPageReference)).thenReturn(true);
when(this.authorization.hasAccess(Right.VIEW, this.aliceReference, this.secondPageReference)).thenReturn(true);
when(this.authorization.hasAccess(Right.VIEW, this.bobReference, this.firstPageReference)).thenReturn(true);
when(this.authorization.hasAccess(Right.VIEW, this.bobReference, this.secondPageReference)).thenReturn(true);
when(this.documentRenderer.render(this.firstPageReference)).thenReturn(this.firstPageRendering);
when(this.documentRenderer.render(this.secondPageReference)).thenReturn(this.secondPageRendering);
}
@Test
void run() throws Exception
{
when(this.requiredSkinExtensionsRecorder.stop()).thenReturn("required skin extensions");
InputStream pdfContent = mock(InputStream.class);
when(this.pdfPrinter.print(this.request)).thenReturn(pdfContent);
this.pdfExportJob.initialize(this.request);
this.pdfExportJob.runInternal();
verify(this.requiredSkinExtensionsRecorder).start();
PDFExportJobStatus jobStatus = this.pdfExportJob.getStatus();
assertEquals("required skin extensions", jobStatus.getRequiredSkinExtensions());
assertEquals(0, jobStatus.getDocumentRenderingResults().size());
TemporaryResourceReference pdfFileReference = jobStatus.getPDFFileReference();
verify(this.temporaryResourceStore).createTemporaryFile(pdfFileReference, pdfContent);
}
@Test
void runClientSide() throws Exception
{
this.request.setServerSide(false);
this.pdfExportJob.initialize(this.request);
this.pdfExportJob.runInternal();
PDFExportJobStatus jobStatus = this.pdfExportJob.getStatus();
assertNull(jobStatus.getPDFFileReference());
TemporaryResourceReference pdfFileReference = jobStatus.getPDFFileReference();
verify(this.temporaryResourceStore, never()).createTemporaryFile(eq(pdfFileReference), any(InputStream.class));
List<DocumentRenderingResult> renderingResults = jobStatus.getDocumentRenderingResults();
assertEquals(2, renderingResults.size());
assertSame(this.firstPageRendering, renderingResults.get(0));
assertSame(this.secondPageRendering, renderingResults.get(1));
}
@Test
void runWithoutDocuments() throws Exception
{
this.request.setDocuments(Collections.emptyList());
this.pdfExportJob.initialize(this.request);
this.pdfExportJob.runInternal();
PDFExportJobStatus jobStatus = this.pdfExportJob.getStatus();
assertNull(jobStatus.getPDFFileReference());
assertNull(jobStatus.getRequiredSkinExtensions());
assertEquals(0, jobStatus.getDocumentRenderingResults().size());
}
}
/*
* See the NOTICE file distributed with this work for additional
* information regarding copyright ownership.
*
* This is free software; you can redistribute it and/or modify it
* under the terms of the GNU Lesser General Public License as
* published by the Free Software Foundation; either version 2.1 of
* the License, or (at your option) any later version.
*
* This software is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.xwiki.export.pdf.internal.macro;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.inject.Inject;
import javax.inject.Named;
import org.apache.commons.lang.StringUtils;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import org.mockito.Mock;
import org.xwiki.bridge.DocumentAccessBridge;
import org.xwiki.export.pdf.internal.job.PDFExportJob;
import org.xwiki.export.pdf.job.PDFExportJobRequest;
import org.xwiki.export.pdf.job.PDFExportJobStatus;
import org.xwiki.export.pdf.job.PDFExportJobStatus.DocumentRenderingResult;
import org.xwiki.export.pdf.macro.PDFTocMacroParameters;
import org.xwiki.job.JobExecutor;
import org.xwiki.job.JobStatusStore;
import org.xwiki.job.event.status.JobStatus;
import org.xwiki.model.reference.DocumentReference;
import org.xwiki.rendering.block.Block;
import org.xwiki.rendering.block.HeaderBlock;
import org.xwiki.rendering.block.WordBlock;
import org.xwiki.rendering.block.XDOM;
import org.xwiki.rendering.internal.renderer.event.EventBlockRenderer;
import org.xwiki.rendering.internal.renderer.event.EventRenderer;
import org.xwiki.rendering.internal.renderer.event.EventRendererFactory;
import org.xwiki.rendering.listener.HeaderLevel;
import org.xwiki.rendering.macro.MacroExecutionException;
import org.xwiki.rendering.parser.Parser;
import org.xwiki.rendering.renderer.BlockRenderer;
import org.xwiki.rendering.renderer.printer.DefaultWikiPrinter;
import org.xwiki.rendering.renderer.reference.link.LinkLabelGenerator;
import org.xwiki.rendering.transformation.MacroTransformationContext;
import org.xwiki.test.annotation.ComponentList;
import org.xwiki.test.junit5.mockito.ComponentTest;
import org.xwiki.test.junit5.mockito.InjectMockComponents;
import org.xwiki.test.junit5.mockito.MockComponent;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.fail;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
/**
* Unit tests for {@link PDFTocMacro}.
*
* @version $Id$
*/
@ComponentTest
@ComponentList({EventBlockRenderer.class, EventRendererFactory.class, EventRenderer.class})
class PDFTocMacroTest
{
@InjectMockComponents
private PDFTocMacro pdfTocMacro;
@MockComponent
private JobStatusStore jobStatusStore;
@MockComponent
private JobExecutor jobExecutor;
@MockComponent
private DocumentAccessBridge documentAccessBridge;
@MockComponent
@Named("plain/1.0")
private Parser plainTextParser;
@MockComponent
private LinkLabelGenerator linkLabelGenerator;
@Inject
@Named("event/1.0")
BlockRenderer eventRenderer;
private PDFTocMacroParameters parameters = new PDFTocMacroParameters();
@Mock
private MacroTransformationContext context;
private List<String> jobId = Arrays.asList("export", "pdf", "1654");
private PDFExportJobRequest request = new PDFExportJobRequest();
private PDFExportJobStatus jobStatus = new PDFExportJobStatus(PDFExportJob.JOB_TYPE, this.request, null, null);
private DocumentReference aliceReference = new DocumentReference("test", "Users", "Alice");
@BeforeEach
void configure()
{
this.parameters.setJobId(StringUtils.join(this.jobId, "/"));
this.request.setUserReference(this.aliceReference);
DocumentReference firstPageReference = new DocumentReference("test", "First", "Page");
XDOM firstXDOM = new XDOM(Collections.singletonList(
new HeaderBlock(Collections.singletonList(new WordBlock("First Heading")), HeaderLevel.LEVEL1)));
this.jobStatus.getDocumentRenderingResults()
.add(new DocumentRenderingResult(firstPageReference, firstXDOM, "first HTML"));
DocumentReference secondPageReference = new DocumentReference("test", "Second", "Page");
XDOM secondXDOM = new XDOM(Collections.singletonList(
new HeaderBlock(Collections.singletonList(new WordBlock("Second Heading")), HeaderLevel.LEVEL2)));
this.jobStatus.getDocumentRenderingResults()
.add(new DocumentRenderingResult(secondPageReference, secondXDOM, "second HTML"));
}
@Test
void executeWithoutJobId()
{
try {
this.pdfTocMacro.execute(new PDFTocMacroParameters(), null, null);
fail();
} catch (MacroExecutionException e) {
assertEquals("The mandatory job id parameter is missing.", e.getMessage());
}
}
@Test
void executeWithoutJobStatus() throws Exception
{
assertEquals(0, this.pdfTocMacro.execute(this.parameters, null, this.context).size());
}
@Test
void executeWithUnexpectedJobStatus() throws Exception
{
JobStatus unexpectedJobStatus = mock(JobStatus.class);
when(this.jobStatusStore.getJobStatus(jobId)).thenReturn(unexpectedJobStatus);
assertEquals(0, this.pdfTocMacro.execute(this.parameters, null, this.context).size());
verify(this.jobExecutor).getJob(jobId);
}
@Test
void executeWithDifferentUser() throws Exception
{
when(this.jobStatusStore.getJobStatus(jobId)).thenReturn(this.jobStatus);
assertEquals(0, this.pdfTocMacro.execute(this.parameters, null, this.context).size());
}
@Test
void execute() throws Exception
{
when(this.jobStatusStore.getJobStatus(jobId)).thenReturn(this.jobStatus);
when(this.documentAccessBridge.getCurrentUserReference()).thenReturn(this.aliceReference);
List<Block> output = this.pdfTocMacro.execute(this.parameters, null, this.context);
List<String> events = Arrays.asList(
"beginList [BULLETED]",
"beginListItem",
"beginLink [Typed = [true] Type = [doc]] [false]",
"onWord [First Heading]",
"endLink [Typed = [true] Type = [doc]] [false]",
"beginList [BULLETED]",
"beginListItem",
"beginLink [Typed = [true] Type = [doc]] [false]",
"onWord [Second Heading]",
"endLink [Typed = [true] Type = [doc]] [false]",
"endListItem",
"endList [BULLETED]",
"endListItem",
"endList [BULLETED]",
""
);
assertBlockEvents(StringUtils.join(events, "\n"), output.get(0));
}
@Test
void executeWithDepth() throws Exception
{
when(this.jobStatusStore.getJobStatus(jobId)).thenReturn(this.jobStatus);
when(this.documentAccessBridge.getCurrentUserReference()).thenReturn(this.aliceReference);
this.parameters.setDepth(1);
List<Block> output = this.pdfTocMacro.execute(this.parameters, null, this.context);
List<String> events = Arrays.asList(
"beginList [BULLETED]",
"beginListItem",
"beginLink [Typed = [true] Type = [doc]] [false]",
"onWord [First Heading]",
"endLink [Typed = [true] Type = [doc]] [false]",
"endListItem",
"endList [BULLETED]",
""
);
assertBlockEvents(StringUtils.join(events, "\n"), output.get(0));
}
private void assertBlockEvents(String expected, Block block)
{
DefaultWikiPrinter printer = new DefaultWikiPrinter();
this.eventRenderer.render(block, printer);
assertEquals(expected, printer.toString());
}
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment