/plugin/src/test/java/com/atlassian/confluence/extra/masterdetail/TestDetailsSummaryMacro.java
Java | 285 lines | 229 code | 54 blank | 2 comment | 4 complexity | 06fa37f08c1a26cda30e42d2e0b05699 MD5 | raw file
- package com.atlassian.confluence.extra.masterdetail;
- import com.atlassian.confluence.api.model.search.SearchContext;
- import com.atlassian.confluence.content.render.xhtml.ConversionContext;
- import com.atlassian.confluence.content.render.xhtml.DefaultConversionContext;
- import com.atlassian.confluence.core.ContentEntityObject;
- import com.atlassian.confluence.core.SpaceContentEntityObject;
- import com.atlassian.confluence.extra.masterdetail.analytics.DetailsSummaryMacroMetricsEvent;
- import com.atlassian.confluence.extra.masterdetail.analytics.DetailsSummaryMacroNestedRecursiveEvent;
- import com.atlassian.confluence.extra.masterdetail.entities.DetailLine;
- import com.atlassian.confluence.extra.masterdetail.entities.PaginatedDetailLines;
- import com.atlassian.confluence.labels.Label;
- import com.atlassian.confluence.labels.LabelManager;
- import com.atlassian.confluence.macro.MacroExecutionException;
- import com.atlassian.confluence.pages.Comment;
- import com.atlassian.confluence.pages.Page;
- import com.atlassian.confluence.plugins.createcontent.api.services.CreateButtonService;
- import com.atlassian.confluence.renderer.PageContext;
- import com.atlassian.confluence.renderer.template.TemplateRenderer;
- import com.atlassian.confluence.spaces.Space;
- import com.atlassian.confluence.spaces.SpaceManager;
- import com.atlassian.confluence.xhtml.api.XhtmlContent;
- import com.atlassian.event.api.EventPublisher;
- import com.atlassian.renderer.RenderContextOutputType;
- import com.atlassian.sal.api.message.I18nResolver;
- import com.atlassian.webresource.api.assembler.PageBuilderService;
- import org.junit.Before;
- import org.junit.Test;
- import org.junit.runner.RunWith;
- import org.mockito.Mock;
- import org.mockito.junit.MockitoJUnitRunner;
- import org.mockito.stubbing.Answer;
- import java.util.ArrayList;
- import java.util.Collections;
- import java.util.List;
- import java.util.Map;
- import static com.atlassian.confluence.content.render.xhtml.ConversionContextOutputDeviceType.DESKTOP;
- import static com.atlassian.confluence.extra.masterdetail.analytics.DetailsSummaryMacroMetricsEvent.Type.MACRO_EXECUTION;
- import static com.atlassian.confluence.macro.Macro.BodyType.NONE;
- import static com.atlassian.confluence.macro.Macro.OutputType.BLOCK;
- import static com.google.common.collect.Maps.newHashMap;
- import static org.apache.commons.lang3.StringUtils.EMPTY;
- import static org.hamcrest.CoreMatchers.containsString;
- import static org.hamcrest.CoreMatchers.nullValue;
- import static org.junit.Assert.assertEquals;
- import static org.junit.Assert.assertThat;
- import static org.mockito.Answers.RETURNS_DEEP_STUBS;
- import static org.mockito.ArgumentMatchers.any;
- import static org.mockito.ArgumentMatchers.anyBoolean;
- import static org.mockito.ArgumentMatchers.anyString;
- import static org.mockito.ArgumentMatchers.eq;
- import static org.mockito.Mockito.mock;
- import static org.mockito.Mockito.times;
- import static org.mockito.Mockito.verify;
- import static org.mockito.Mockito.when;
- @RunWith(MockitoJUnitRunner.class)
- public class TestDetailsSummaryMacro {
- private static final String TEST_SPACE_KEY = "TST";
- private static final Space TEST_SPACE = new Space(TEST_SPACE_KEY);
- @Mock
- private I18nResolver i18nResolver;
- @Mock
- private XhtmlContent xhtmlContent;
- @Mock
- private LabelManager labelManager;
- @Mock(answer = RETURNS_DEEP_STUBS)
- private PageBuilderService pageBuilderService;
- @Mock
- private SpaceManager spaceManager;
- @Mock
- private CreateButtonService createButtonService;
- @Mock
- private TemplateRenderer templateRenderer;
- @Mock
- private ContentRetriever contentRetriever;
- @Mock
- private DetailsSummaryBuilder detailsSummaryBuilder;
- @Mock
- private EventPublisher eventPublisher;
- @Mock
- private MasterDetailConfigurator configurator;
- private DetailsSummaryMacro macro;
- private final Map<String, String> macroParameters = newHashMap();
- private final Label myLabel = new Label("mylabel");
- @Before
- public void setup() {
- when(i18nResolver.getText(anyString())).thenAnswer(RETURN_FIRST_ARGUMENT);
- when(spaceManager.getSpace(TEST_SPACE_KEY)).thenReturn(TEST_SPACE);
- when(configurator.getPagePropertiesReportMaximumRecursionDepth()).thenReturn(2);
- when(detailsSummaryBuilder.getPaginatedDetailLines(any(DetailsSummaryParameters.class), anyBoolean(), any(DetailsSummaryMacroMetricsEvent.Builder.class), any(ConversionContext.class))).thenAnswer((invocationOnMock) -> {
- DetailsSummaryParameters params = invocationOnMock.getArgument(0);
- params.setTotalRenderedLines(90);
- return mock(PaginatedDetailLines.class);
- });
- macro = new DetailsSummaryMacro(
- i18nResolver, xhtmlContent,
- spaceManager, pageBuilderService, createButtonService, contentRetriever, detailsSummaryBuilder,
- templateRenderer, eventPublisher, configurator);
- }
- @Test
- public void errorsFromMacroExceptionsAreWrapped() throws Exception {
- ConversionContext conversionContext = conversionContext();
- String errorKey = "it.is.bad";
- when(contentRetriever.getContentWithMetaData(any(), any(Boolean.class), any(SearchContext.class), any(DetailsSummaryMacroMetricsEvent.Builder.class)))
- .thenThrow(new MacroExecutionException(errorKey));
- String expectedErrorMarkup = expectedErrorMarkup(errorKey);
- assertEquals(expectedErrorMarkup, macro.execute(macroParameters, EMPTY, conversionContext));
- }
- @Test
- public void errorShownIfRenderedOnNonSpaceContentEntityObjects() throws Exception {
- assertEquals(expectedErrorMarkup("detailssummary.error.must.be.inside.space"),
- macro.execute(macroParameters, EMPTY, conversionContextForNonSpaceContentEntity()));
- }
- @Test
- public void macroGeneratesBlockHtml() {
- assertEquals(BLOCK, macro.getOutputType());
- }
- @Test
- public void macroHasNoBody() {
- assertEquals(NONE, macro.getBodyType());
- }
- @Test
- public void blankExperienceWithButton() throws Exception {
- String contentBlueprintId = "12345";
- String blueprintModuleCompleteKey = "com.atlassian.confluence:foo";
- String buttonLabelKey = "blank.button.label";
- macroParameters.put("blueprintModuleCompleteKey", blueprintModuleCompleteKey);
- macroParameters.put("contentBlueprintId", contentBlueprintId);
- macroParameters.put("createButtonLabel", buttonLabelKey);
- PaginatedDetailLines paginatedDetails = mock(PaginatedDetailLines.class);
- when(contentRetriever.getContentWithMetaData(any(), any(Boolean.class), any(SearchContext.class), any(DetailsSummaryMacroMetricsEvent.Builder.class)))
- .thenReturn(new ContentRetrieverResult(Collections.emptyList(), false));
- macro.execute(macroParameters, EMPTY, conversionContext());
- verify(createButtonService).renderBlueprintButton(TEST_SPACE, contentBlueprintId, blueprintModuleCompleteKey, buttonLabelKey, null);
- }
- @Test
- public void testPDFExport() throws Exception {
- testExport(RenderContextOutputType.PDF, 50, 1000, 1, true);
- }
- @Test
- public void testWordExport() throws Exception {
- testExport(RenderContextOutputType.WORD, 50, 1000, 1, true);
- }
- @Test
- public void testHTMLExport() throws Exception {
- testExport(RenderContextOutputType.HTML_EXPORT, 50, 1000, 1, true);
- }
- @Test
- public void testNormalDisplay() throws Exception {
- testExport(RenderContextOutputType.DISPLAY, 50, 30, 2, false);
- }
- @Test
- public void testClientSideRender() throws Exception {
- testExport(RenderContextOutputType.DISPLAY, 50, 30, 0, true);
- }
- @Test
- public void testNestedRecursionDetection() throws Exception {
- when(contentRetriever.getContentWithMetaData(any(), any(Boolean.class), any(SearchContext.class), any(DetailsSummaryMacroMetricsEvent.Builder.class)))
- .thenReturn(new ContentRetrieverResult(Collections.emptyList(), false));
- when(i18nResolver.getText(eq("detailssummary.error.nested.recursive"))).thenReturn("Error");
- ConversionContext conversionContext = conversionContext(RenderContextOutputType.DISPLAY);
- conversionContext.disableAsyncRenderSafe(); // For server side rendering
- macro.execute(macroParameters, "", conversionContext);
- // Check that conversion context is reset after rendering macro
- assertThat(conversionContext.getProperty("details_summary_depth"), nullValue());
- // Simulate nested calls by setting recursion depth
- conversionContext.setProperty("details_summary_depth", 2);
- assertThat(macro.execute(macroParameters, "", conversionContext), containsString("Error")); // Recursion depth hit, body should contain error
- verify(eventPublisher, times(1)).publish(any(DetailsSummaryMacroNestedRecursiveEvent.class));
- }
- public void testExport(String outputType, int numOfContent, int expectedMaxRow, int expectedPages, boolean isAsyncRenderSafe) throws Exception {
- String contentBlueprintId = "12345";
- String blueprintModuleCompleteKey = "com.atlassian.confluence:foo";
- String buttonLabelKey = "blank.button.label";
- macroParameters.put("blueprintModuleCompleteKey", blueprintModuleCompleteKey);
- macroParameters.put("contentBlueprintId", contentBlueprintId);
- macroParameters.put("createButtonLabel", buttonLabelKey);
- List<ContentEntityObject> contents = new ArrayList<>();
- List<DetailLine> detailLines = new ArrayList<>();
- int i = 1;
- while (i <= numOfContent) {
- Page content = new Page();
- content.setTitle("title " + i);
- contents.add(content);
- DetailLine detail = new DetailLine(content, new ArrayList<>());
- detail.setTitle("title " + i);
- detailLines.add(detail);
- i++;
- }
- ContentRetrieverResult contentRetrieverResult = new ContentRetrieverResult(contents, false);
- when(contentRetriever.getContentWithMetaData(any(), any(Boolean.class), any(SearchContext.class), any(DetailsSummaryMacroMetricsEvent.Builder.class))).thenReturn(contentRetrieverResult);
- PaginatedDetailLines paginatedDetails = mock(PaginatedDetailLines.class);
- when(paginatedDetails.getDetailLines()).thenReturn(detailLines);
- when(detailsSummaryBuilder.getPaginatedDetailLines(any(DetailsSummaryParameters.class), anyBoolean(), any(DetailsSummaryMacroMetricsEvent.Builder.class), any(ConversionContext.class)))
- .thenAnswer((invocationOnMock) -> {
- DetailsSummaryParameters params = invocationOnMock.getArgument(0);
- params.setTotalRenderedLines(numOfContent);
- return paginatedDetails;
- });
- ConversionContext context = conversionContext(outputType);
- if (!isAsyncRenderSafe) {
- context.disableAsyncRenderSafe();
- }
- Map<String, Object> templateModel = macro.buildTemplateModel(macroParameters, context, DetailsSummaryMacroMetricsEvent
- .builder(MACRO_EXECUTION)
- .macroOutputType(context.getOutputType()));
- assertEquals(expectedMaxRow, (int) templateModel.get("pageSize"));
- assertEquals(expectedPages, (int) templateModel.get("totalPages"));
- }
- private String expectedErrorMarkup(String message) {
- return "<div class=\"error\"><span class=\"error\">" + message + "</span> </div>";
- }
- private static final Answer<String> RETURN_FIRST_ARGUMENT = invocationOnMock -> invocationOnMock.getArguments()[0].toString();
- public static ConversionContext conversionContextForNonSpaceContentEntity() {
- return new DefaultConversionContext(new Comment().toPageContext());
- }
- public static ConversionContext conversionContext() {
- return conversionContext(null);
- }
- public static ConversionContext conversionContext(String outputType) {
- SpaceContentEntityObject page = mock(SpaceContentEntityObject.class);
- PageContext pageContext = mock(PageContext.class);
- when(pageContext.getEntity()).thenReturn(page);
- when(pageContext.getSpaceKey()).thenReturn(TEST_SPACE_KEY);
- when(pageContext.getOutputDeviceType()).thenReturn(DESKTOP);
- if (outputType != null) {
- when(pageContext.getOutputType()).thenReturn(outputType);
- }
- when(pageContext.toSearchContext()).thenReturn(SearchContext.builder());
- return new DefaultConversionContext(pageContext);
- }
- }