PageRenderTime 33ms CodeModel.GetById 13ms RepoModel.GetById 0ms app.codeStats 1ms

/plugin/src/test/java/com/atlassian/confluence/extra/masterdetail/TestDetailsSummaryMacro.java

https://bitbucket.org/atlassian/confluence-masterdetail-plugin
Java | 285 lines | 229 code | 54 blank | 2 comment | 4 complexity | 06fa37f08c1a26cda30e42d2e0b05699 MD5 | raw file
  1. package com.atlassian.confluence.extra.masterdetail;
  2. import com.atlassian.confluence.api.model.search.SearchContext;
  3. import com.atlassian.confluence.content.render.xhtml.ConversionContext;
  4. import com.atlassian.confluence.content.render.xhtml.DefaultConversionContext;
  5. import com.atlassian.confluence.core.ContentEntityObject;
  6. import com.atlassian.confluence.core.SpaceContentEntityObject;
  7. import com.atlassian.confluence.extra.masterdetail.analytics.DetailsSummaryMacroMetricsEvent;
  8. import com.atlassian.confluence.extra.masterdetail.analytics.DetailsSummaryMacroNestedRecursiveEvent;
  9. import com.atlassian.confluence.extra.masterdetail.entities.DetailLine;
  10. import com.atlassian.confluence.extra.masterdetail.entities.PaginatedDetailLines;
  11. import com.atlassian.confluence.labels.Label;
  12. import com.atlassian.confluence.labels.LabelManager;
  13. import com.atlassian.confluence.macro.MacroExecutionException;
  14. import com.atlassian.confluence.pages.Comment;
  15. import com.atlassian.confluence.pages.Page;
  16. import com.atlassian.confluence.plugins.createcontent.api.services.CreateButtonService;
  17. import com.atlassian.confluence.renderer.PageContext;
  18. import com.atlassian.confluence.renderer.template.TemplateRenderer;
  19. import com.atlassian.confluence.spaces.Space;
  20. import com.atlassian.confluence.spaces.SpaceManager;
  21. import com.atlassian.confluence.xhtml.api.XhtmlContent;
  22. import com.atlassian.event.api.EventPublisher;
  23. import com.atlassian.renderer.RenderContextOutputType;
  24. import com.atlassian.sal.api.message.I18nResolver;
  25. import com.atlassian.webresource.api.assembler.PageBuilderService;
  26. import org.junit.Before;
  27. import org.junit.Test;
  28. import org.junit.runner.RunWith;
  29. import org.mockito.Mock;
  30. import org.mockito.junit.MockitoJUnitRunner;
  31. import org.mockito.stubbing.Answer;
  32. import java.util.ArrayList;
  33. import java.util.Collections;
  34. import java.util.List;
  35. import java.util.Map;
  36. import static com.atlassian.confluence.content.render.xhtml.ConversionContextOutputDeviceType.DESKTOP;
  37. import static com.atlassian.confluence.extra.masterdetail.analytics.DetailsSummaryMacroMetricsEvent.Type.MACRO_EXECUTION;
  38. import static com.atlassian.confluence.macro.Macro.BodyType.NONE;
  39. import static com.atlassian.confluence.macro.Macro.OutputType.BLOCK;
  40. import static com.google.common.collect.Maps.newHashMap;
  41. import static org.apache.commons.lang3.StringUtils.EMPTY;
  42. import static org.hamcrest.CoreMatchers.containsString;
  43. import static org.hamcrest.CoreMatchers.nullValue;
  44. import static org.junit.Assert.assertEquals;
  45. import static org.junit.Assert.assertThat;
  46. import static org.mockito.Answers.RETURNS_DEEP_STUBS;
  47. import static org.mockito.ArgumentMatchers.any;
  48. import static org.mockito.ArgumentMatchers.anyBoolean;
  49. import static org.mockito.ArgumentMatchers.anyString;
  50. import static org.mockito.ArgumentMatchers.eq;
  51. import static org.mockito.Mockito.mock;
  52. import static org.mockito.Mockito.times;
  53. import static org.mockito.Mockito.verify;
  54. import static org.mockito.Mockito.when;
  55. @RunWith(MockitoJUnitRunner.class)
  56. public class TestDetailsSummaryMacro {
  57. private static final String TEST_SPACE_KEY = "TST";
  58. private static final Space TEST_SPACE = new Space(TEST_SPACE_KEY);
  59. @Mock
  60. private I18nResolver i18nResolver;
  61. @Mock
  62. private XhtmlContent xhtmlContent;
  63. @Mock
  64. private LabelManager labelManager;
  65. @Mock(answer = RETURNS_DEEP_STUBS)
  66. private PageBuilderService pageBuilderService;
  67. @Mock
  68. private SpaceManager spaceManager;
  69. @Mock
  70. private CreateButtonService createButtonService;
  71. @Mock
  72. private TemplateRenderer templateRenderer;
  73. @Mock
  74. private ContentRetriever contentRetriever;
  75. @Mock
  76. private DetailsSummaryBuilder detailsSummaryBuilder;
  77. @Mock
  78. private EventPublisher eventPublisher;
  79. @Mock
  80. private MasterDetailConfigurator configurator;
  81. private DetailsSummaryMacro macro;
  82. private final Map<String, String> macroParameters = newHashMap();
  83. private final Label myLabel = new Label("mylabel");
  84. @Before
  85. public void setup() {
  86. when(i18nResolver.getText(anyString())).thenAnswer(RETURN_FIRST_ARGUMENT);
  87. when(spaceManager.getSpace(TEST_SPACE_KEY)).thenReturn(TEST_SPACE);
  88. when(configurator.getPagePropertiesReportMaximumRecursionDepth()).thenReturn(2);
  89. when(detailsSummaryBuilder.getPaginatedDetailLines(any(DetailsSummaryParameters.class), anyBoolean(), any(DetailsSummaryMacroMetricsEvent.Builder.class), any(ConversionContext.class))).thenAnswer((invocationOnMock) -> {
  90. DetailsSummaryParameters params = invocationOnMock.getArgument(0);
  91. params.setTotalRenderedLines(90);
  92. return mock(PaginatedDetailLines.class);
  93. });
  94. macro = new DetailsSummaryMacro(
  95. i18nResolver, xhtmlContent,
  96. spaceManager, pageBuilderService, createButtonService, contentRetriever, detailsSummaryBuilder,
  97. templateRenderer, eventPublisher, configurator);
  98. }
  99. @Test
  100. public void errorsFromMacroExceptionsAreWrapped() throws Exception {
  101. ConversionContext conversionContext = conversionContext();
  102. String errorKey = "it.is.bad";
  103. when(contentRetriever.getContentWithMetaData(any(), any(Boolean.class), any(SearchContext.class), any(DetailsSummaryMacroMetricsEvent.Builder.class)))
  104. .thenThrow(new MacroExecutionException(errorKey));
  105. String expectedErrorMarkup = expectedErrorMarkup(errorKey);
  106. assertEquals(expectedErrorMarkup, macro.execute(macroParameters, EMPTY, conversionContext));
  107. }
  108. @Test
  109. public void errorShownIfRenderedOnNonSpaceContentEntityObjects() throws Exception {
  110. assertEquals(expectedErrorMarkup("detailssummary.error.must.be.inside.space"),
  111. macro.execute(macroParameters, EMPTY, conversionContextForNonSpaceContentEntity()));
  112. }
  113. @Test
  114. public void macroGeneratesBlockHtml() {
  115. assertEquals(BLOCK, macro.getOutputType());
  116. }
  117. @Test
  118. public void macroHasNoBody() {
  119. assertEquals(NONE, macro.getBodyType());
  120. }
  121. @Test
  122. public void blankExperienceWithButton() throws Exception {
  123. String contentBlueprintId = "12345";
  124. String blueprintModuleCompleteKey = "com.atlassian.confluence:foo";
  125. String buttonLabelKey = "blank.button.label";
  126. macroParameters.put("blueprintModuleCompleteKey", blueprintModuleCompleteKey);
  127. macroParameters.put("contentBlueprintId", contentBlueprintId);
  128. macroParameters.put("createButtonLabel", buttonLabelKey);
  129. PaginatedDetailLines paginatedDetails = mock(PaginatedDetailLines.class);
  130. when(contentRetriever.getContentWithMetaData(any(), any(Boolean.class), any(SearchContext.class), any(DetailsSummaryMacroMetricsEvent.Builder.class)))
  131. .thenReturn(new ContentRetrieverResult(Collections.emptyList(), false));
  132. macro.execute(macroParameters, EMPTY, conversionContext());
  133. verify(createButtonService).renderBlueprintButton(TEST_SPACE, contentBlueprintId, blueprintModuleCompleteKey, buttonLabelKey, null);
  134. }
  135. @Test
  136. public void testPDFExport() throws Exception {
  137. testExport(RenderContextOutputType.PDF, 50, 1000, 1, true);
  138. }
  139. @Test
  140. public void testWordExport() throws Exception {
  141. testExport(RenderContextOutputType.WORD, 50, 1000, 1, true);
  142. }
  143. @Test
  144. public void testHTMLExport() throws Exception {
  145. testExport(RenderContextOutputType.HTML_EXPORT, 50, 1000, 1, true);
  146. }
  147. @Test
  148. public void testNormalDisplay() throws Exception {
  149. testExport(RenderContextOutputType.DISPLAY, 50, 30, 2, false);
  150. }
  151. @Test
  152. public void testClientSideRender() throws Exception {
  153. testExport(RenderContextOutputType.DISPLAY, 50, 30, 0, true);
  154. }
  155. @Test
  156. public void testNestedRecursionDetection() throws Exception {
  157. when(contentRetriever.getContentWithMetaData(any(), any(Boolean.class), any(SearchContext.class), any(DetailsSummaryMacroMetricsEvent.Builder.class)))
  158. .thenReturn(new ContentRetrieverResult(Collections.emptyList(), false));
  159. when(i18nResolver.getText(eq("detailssummary.error.nested.recursive"))).thenReturn("Error");
  160. ConversionContext conversionContext = conversionContext(RenderContextOutputType.DISPLAY);
  161. conversionContext.disableAsyncRenderSafe(); // For server side rendering
  162. macro.execute(macroParameters, "", conversionContext);
  163. // Check that conversion context is reset after rendering macro
  164. assertThat(conversionContext.getProperty("details_summary_depth"), nullValue());
  165. // Simulate nested calls by setting recursion depth
  166. conversionContext.setProperty("details_summary_depth", 2);
  167. assertThat(macro.execute(macroParameters, "", conversionContext), containsString("Error")); // Recursion depth hit, body should contain error
  168. verify(eventPublisher, times(1)).publish(any(DetailsSummaryMacroNestedRecursiveEvent.class));
  169. }
  170. public void testExport(String outputType, int numOfContent, int expectedMaxRow, int expectedPages, boolean isAsyncRenderSafe) throws Exception {
  171. String contentBlueprintId = "12345";
  172. String blueprintModuleCompleteKey = "com.atlassian.confluence:foo";
  173. String buttonLabelKey = "blank.button.label";
  174. macroParameters.put("blueprintModuleCompleteKey", blueprintModuleCompleteKey);
  175. macroParameters.put("contentBlueprintId", contentBlueprintId);
  176. macroParameters.put("createButtonLabel", buttonLabelKey);
  177. List<ContentEntityObject> contents = new ArrayList<>();
  178. List<DetailLine> detailLines = new ArrayList<>();
  179. int i = 1;
  180. while (i <= numOfContent) {
  181. Page content = new Page();
  182. content.setTitle("title " + i);
  183. contents.add(content);
  184. DetailLine detail = new DetailLine(content, new ArrayList<>());
  185. detail.setTitle("title " + i);
  186. detailLines.add(detail);
  187. i++;
  188. }
  189. ContentRetrieverResult contentRetrieverResult = new ContentRetrieverResult(contents, false);
  190. when(contentRetriever.getContentWithMetaData(any(), any(Boolean.class), any(SearchContext.class), any(DetailsSummaryMacroMetricsEvent.Builder.class))).thenReturn(contentRetrieverResult);
  191. PaginatedDetailLines paginatedDetails = mock(PaginatedDetailLines.class);
  192. when(paginatedDetails.getDetailLines()).thenReturn(detailLines);
  193. when(detailsSummaryBuilder.getPaginatedDetailLines(any(DetailsSummaryParameters.class), anyBoolean(), any(DetailsSummaryMacroMetricsEvent.Builder.class), any(ConversionContext.class)))
  194. .thenAnswer((invocationOnMock) -> {
  195. DetailsSummaryParameters params = invocationOnMock.getArgument(0);
  196. params.setTotalRenderedLines(numOfContent);
  197. return paginatedDetails;
  198. });
  199. ConversionContext context = conversionContext(outputType);
  200. if (!isAsyncRenderSafe) {
  201. context.disableAsyncRenderSafe();
  202. }
  203. Map<String, Object> templateModel = macro.buildTemplateModel(macroParameters, context, DetailsSummaryMacroMetricsEvent
  204. .builder(MACRO_EXECUTION)
  205. .macroOutputType(context.getOutputType()));
  206. assertEquals(expectedMaxRow, (int) templateModel.get("pageSize"));
  207. assertEquals(expectedPages, (int) templateModel.get("totalPages"));
  208. }
  209. private String expectedErrorMarkup(String message) {
  210. return "<div class=\"error\"><span class=\"error\">" + message + "</span> </div>";
  211. }
  212. private static final Answer<String> RETURN_FIRST_ARGUMENT = invocationOnMock -> invocationOnMock.getArguments()[0].toString();
  213. public static ConversionContext conversionContextForNonSpaceContentEntity() {
  214. return new DefaultConversionContext(new Comment().toPageContext());
  215. }
  216. public static ConversionContext conversionContext() {
  217. return conversionContext(null);
  218. }
  219. public static ConversionContext conversionContext(String outputType) {
  220. SpaceContentEntityObject page = mock(SpaceContentEntityObject.class);
  221. PageContext pageContext = mock(PageContext.class);
  222. when(pageContext.getEntity()).thenReturn(page);
  223. when(pageContext.getSpaceKey()).thenReturn(TEST_SPACE_KEY);
  224. when(pageContext.getOutputDeviceType()).thenReturn(DESKTOP);
  225. if (outputType != null) {
  226. when(pageContext.getOutputType()).thenReturn(outputType);
  227. }
  228. when(pageContext.toSearchContext()).thenReturn(SearchContext.builder());
  229. return new DefaultConversionContext(pageContext);
  230. }
  231. }