From 4d70a989b89638ac932894052d2dc2a4f88deb1f Mon Sep 17 00:00:00 2001 From: Michael Hoennig Date: Wed, 28 Oct 2020 16:28:34 +0100 Subject: [PATCH] jbake issue #652: prev/next post navigation only uses published posts --- .../org/jbake/render/DocumentsRenderer.java | 48 +++++-- .../jbake/render/DocumentsRendererTest.java | 125 +++++++++--------- 2 files changed, 98 insertions(+), 75 deletions(-) diff --git a/jbake-core/src/main/java/org/jbake/render/DocumentsRenderer.java b/jbake-core/src/main/java/org/jbake/render/DocumentsRenderer.java index 78a4a4760..e439a0eba 100644 --- a/jbake-core/src/main/java/org/jbake/render/DocumentsRenderer.java +++ b/jbake-core/src/main/java/org/jbake/render/DocumentsRenderer.java @@ -22,7 +22,7 @@ public int render(Renderer renderer, ContentStore db, JBakeConfiguration config) int renderedCount = 0; final List errors = new LinkedList<>(); for (String docType : DocumentTypes.getDocumentTypes()) { - DocumentList documentList = db.getUnrenderedContent(docType); + final DocumentList documentList = db.getUnrenderedContent(docType); if (documentList == null) { continue; @@ -30,30 +30,34 @@ public int render(Renderer renderer, ContentStore db, JBakeConfiguration config) int index = 0; - Map nextDocument = null; + Map nextDocumentForNav = null; while (index < documentList.size()) { try { - Map document = documentList.get(index); - document.put("nextContent", null); - document.put("previousContent", null); + final Map documentToRender = documentList.get(index); + documentToRender.put("nextContent", null); + documentToRender.put("previousContent", null); - if (index > 0) { - document.put("nextContent", getContentForNav(nextDocument)); + if (nextDocumentForNav != null) { + documentToRender.put("nextContent", getContentForNav(nextDocumentForNav)); } if (index < documentList.size() - 1) { - Map tempNext = documentList.get(index + 1); - document.put("previousContent", getContentForNav(tempNext)); + Map prevDocumentForNav = findPrevPublishedDocument(documentList, index); + if (prevDocumentForNav != null) { + documentToRender.put("previousContent", getContentForNav(prevDocumentForNav)); + } } - nextDocument = document; + if (isPublished(documentToRender)) { + nextDocumentForNav = documentToRender; + } - renderer.render(document); + renderer.render(documentToRender); renderedCount++; } catch (Exception e) { - errors.add(e.getMessage()); + errors.add(e.getMessage() != null ? e.getMessage() : e.getClass().getName()); } index++; @@ -73,11 +77,27 @@ public int render(Renderer renderer, ContentStore db, JBakeConfiguration config) } } + private Map findPrevPublishedDocument(DocumentList documentList, int index) { + for ( int prevDocIndex = index+1; prevDocIndex < documentList.size(); ++prevDocIndex ) { + Map prevDocument = documentList.get(prevDocIndex); + if (isPublished(prevDocument)) { + return prevDocument; + } + } + return null; + } + + private boolean isPublished(Map document) { + // Attributes.Status.PUBLISHED_DATE cannot occur here + // because it's converted TO either PUBLISHED or DRAFT in the Crawler. + return Attributes.Status.PUBLISHED.equals(document.get(Attributes.STATUS)); + } + /** * Creates a simple content model to use in individual post navigations. * - * @param document - * @return + * @param document original + * @return navigation model for the 'document' */ private Map getContentForNav(Map document) { Map navDocument = new HashMap<>(); diff --git a/jbake-core/src/test/java/org/jbake/render/DocumentsRendererTest.java b/jbake-core/src/test/java/org/jbake/render/DocumentsRendererTest.java index cc57bab39..139b3bf74 100644 --- a/jbake-core/src/test/java/org/jbake/render/DocumentsRendererTest.java +++ b/jbake-core/src/test/java/org/jbake/render/DocumentsRendererTest.java @@ -10,6 +10,7 @@ import org.junit.Before; import org.junit.Rule; import org.junit.Test; +import org.junit.jupiter.api.Assertions; import org.junit.rules.ExpectedException; import org.mockito.ArgumentCaptor; import org.mockito.ArgumentMatchers; @@ -19,6 +20,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Collectors; import static org.assertj.core.api.Assertions.assertThat; import static org.mockito.ArgumentMatchers.anyString; @@ -30,9 +32,7 @@ public class DocumentsRendererTest { - @Rule - public ExpectedException exception = ExpectedException.none(); - private DocumentsRenderer documentsRenderer; + public DocumentsRenderer documentsRenderer; private ContentStore db; private Renderer renderer; private JBakeConfiguration configuration; @@ -87,94 +87,97 @@ public void shouldReturnCountOfProcessedDocuments() throws Exception { } @Test - public void shouldThrowAnExceptionWithCollectedErrorMessages() throws Exception { + public void shouldThrowAnExceptionWithCollectedErrorMessages() { String fakeExceptionMessage = "fake exception"; - // expect - exception.expect(RenderingException.class); - exception.expectMessage(fakeExceptionMessage + "\n" + fakeExceptionMessage); - - // given - DocumentTypes.addDocumentType("customType"); - DocumentList documentList = new DocumentList(); - HashMap document = emptyDocument(); - HashMap document2 = emptyDocument(); - documentList.add(document); - documentList.add(document2); - - // throw an exception for every call of renderer's render method - doThrow(new Exception(fakeExceptionMessage)).when(renderer).render(ArgumentMatchers.anyMap()); - when(db.getUnrenderedContent(anyString())).thenReturn(emptyDocumentList); - when(db.getUnrenderedContent("customType")).thenReturn(documentList); - - // when - int renderResponse = documentsRenderer.render(renderer, db, configuration); - - // then - assertThat(renderResponse).isEqualTo(2); + // expect + Assertions.assertThrows( + RenderingException.class, () -> { + + // given + DocumentTypes.addDocumentType("customType"); + + DocumentList documentList = new DocumentList(); + HashMap document = emptyDocument(); + HashMap document2 = emptyDocument(); + documentList.add(document); + documentList.add(document2); + + // throw an exception for every call of renderer's render method + doThrow(new Exception(fakeExceptionMessage)).when(renderer).render(ArgumentMatchers.anyMap()); + when(db.getUnrenderedContent(anyString())).thenReturn(emptyDocumentList); + when(db.getUnrenderedContent("customType")).thenReturn(documentList); + + // when + int renderResponse = documentsRenderer.render(renderer, db, configuration); + + // then + assertThat(renderResponse).isEqualTo(2); + }, + fakeExceptionMessage + "\n" + fakeExceptionMessage + ); } @Test public void shouldContainPostNavigation() throws Exception { + // given DocumentTypes.addDocumentType("customType"); - String first = "First Document"; - String second = "Second Document"; - String third = "Third Document"; - String fourth = "Fourth Document"; + String firstTitle = "First Document"; + String secondTitleIsDraft = "Second Document (draft)"; + String thirdTitle = "Third Document"; + String fourthTitle = "Fourth Document"; DocumentList documents = new DocumentList(); - documents.add(simpleDocument(fourth)); - documents.add(simpleDocument(third)); - documents.add(simpleDocument(second)); - documents.add(simpleDocument(first)); + // Attributes.Status.PUBLISHED_DATE cannot occur here + // because it's converted TO either PUBLISHED or DRAFT in the Crawler. + documents.add(simpleDocument(fourthTitle, Attributes.Status.PUBLISHED)); + documents.add(simpleDocument(thirdTitle, Attributes.Status.PUBLISHED)); + documents.add(simpleDocument(secondTitleIsDraft, Attributes.Status.DRAFT)); + documents.add(simpleDocument(firstTitle, Attributes.Status.PUBLISHED)); when(db.getUnrenderedContent("customType")).thenReturn(documents); + // when int renderResponse = documentsRenderer.render(renderer, db, configuration); - Map fourthDoc = simpleDocument(fourth); - fourthDoc.put("previousContent", simpleDocument(third)); - fourthDoc.put("nextContent", null); - - Map thirdDoc = simpleDocument(third); - thirdDoc.put("nextContent", simpleDocument(fourth)); - thirdDoc.put("previousContent", simpleDocument(second)); - - Map secondDoc = simpleDocument(second); - secondDoc.put("nextContent", simpleDocument(third)); - secondDoc.put("previousContent", simpleDocument(first)); - - Map firstDoc = simpleDocument(first); - firstDoc.put("nextContent", simpleDocument(second)); - firstDoc.put("previousContent", null); - + // then verify(renderer, times(4)).render(argument.capture()); - List> maps = argument.getAllValues(); - - assertThat(maps).contains(fourthDoc); - - assertThat(maps).contains(thirdDoc); - - assertThat(maps).contains(secondDoc); + final Map> renderedDocs = asTitleToDocMap(argument.getAllValues()); + assertDocumentNavigation(renderedDocs.get(firstTitle), null, thirdTitle); + assertDocumentNavigation(renderedDocs.get(secondTitleIsDraft), firstTitle, thirdTitle); + assertDocumentNavigation(renderedDocs.get(thirdTitle), firstTitle, fourthTitle); + assertDocumentNavigation(renderedDocs.get(fourthTitle), thirdTitle, null); + assertThat(renderResponse).isEqualTo(4); + } - assertThat(maps).contains(firstDoc); + private void assertDocumentNavigation( + final Map renderedDoc, + final String prevDocumentTitle, String nextDocumentTitle) { + assertThat(renderedDoc).flatExtracting( + "previousContent." + Attributes.TITLE, + "nextContent." + Attributes.TITLE) + .containsExactly(prevDocumentTitle, nextDocumentTitle); + } - assertThat(renderResponse).isEqualTo(4); + private Map> asTitleToDocMap(List> values) { + return values.stream() + .collect(Collectors.toMap(doc -> doc.get(Attributes.TITLE).toString(), doc -> doc)); } private HashMap emptyDocument() { return new HashMap<>(); } - private Map simpleDocument(String title) { + private Map simpleDocument(String title, String status) { Map simpleDoc = new HashMap<>(); String uri = title.replace(" ", "_"); simpleDoc.put(Attributes.NO_EXTENSION_URI, uri); simpleDoc.put(Attributes.URI, uri); simpleDoc.put(Attributes.TITLE, title); + simpleDoc.put(Attributes.STATUS, status); return simpleDoc; } -} \ No newline at end of file +}