Skip to content

Commit ba50186

Browse files
author
Maple Buice
committed
Update TruncateHtmlFilter to execute jinjava before truncating
Uses the same logic as StripTagsFilter
1 parent 0894c2d commit ba50186

File tree

2 files changed

+81
-5
lines changed

2 files changed

+81
-5
lines changed

src/main/java/com/hubspot/jinjava/lib/filter/TruncateHtmlFilter.java

Lines changed: 18 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@
33
import com.hubspot.jinjava.doc.annotations.JinjavaDoc;
44
import com.hubspot.jinjava.doc.annotations.JinjavaParam;
55
import com.hubspot.jinjava.doc.annotations.JinjavaSnippet;
6+
import com.hubspot.jinjava.interpret.DeferredValueException;
67
import com.hubspot.jinjava.interpret.InvalidArgumentException;
78
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
89
import com.hubspot.jinjava.interpret.TemplateError;
@@ -141,7 +142,21 @@ public Object filter(Object var, JinjavaInterpreter interpreter, String... args)
141142
killwords = BooleanUtils.toBoolean(args[2]);
142143
}
143144

144-
Document dom = Jsoup.parseBodyFragment((String) var);
145+
int numDeferredTokensStart = interpreter.getContext().getDeferredTokens().size();
146+
147+
String val;
148+
try (
149+
JinjavaInterpreter.InterpreterScopeClosable ignored = interpreter.enterScope()
150+
) {
151+
val = interpreter.renderFlat((String) var);
152+
if (
153+
interpreter.getContext().getDeferredTokens().size() > numDeferredTokensStart
154+
) {
155+
throw new DeferredValueException("Deferred in TruncateHtmlFilter");
156+
}
157+
}
158+
159+
Document dom = Jsoup.parseBodyFragment(val);
145160
ContentTruncatingNodeVisitor visitor = new ContentTruncatingNodeVisitor(
146161
length,
147162
ends,
@@ -171,8 +186,7 @@ private static class ContentTruncatingNodeVisitor implements NodeVisitor {
171186

172187
@Override
173188
public void head(Node node, int depth) {
174-
if (node instanceof TextNode) {
175-
TextNode text = (TextNode) node;
189+
if (node instanceof TextNode text) {
176190
String textContent = text.text();
177191

178192
if (textLen >= maxTextLen) {
@@ -193,8 +207,7 @@ public void head(Node node, int depth) {
193207

194208
@Override
195209
public void tail(Node node, int depth) {
196-
if (node instanceof Element) {
197-
Element el = (Element) node;
210+
if (node instanceof Element el) {
198211
if (StringUtils.isBlank(el.text())) {
199212
el.addClass("__deleteme");
200213
}

src/test/java/com/hubspot/jinjava/lib/filter/TruncateHtmlFilterTest.java

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,22 @@
11
package com.hubspot.jinjava.lib.filter;
22

33
import static org.assertj.core.api.Assertions.assertThat;
4+
import static org.assertj.core.api.Assertions.assertThatThrownBy;
5+
import static org.mockito.Mockito.mock;
6+
import static org.mockito.Mockito.when;
47

58
import com.google.common.collect.ImmutableMap;
69
import com.google.common.io.Resources;
710
import com.hubspot.jinjava.BaseInterpretingTest;
11+
import com.hubspot.jinjava.interpret.Context;
12+
import com.hubspot.jinjava.interpret.DeferredValueException;
13+
import com.hubspot.jinjava.interpret.JinjavaInterpreter;
14+
import com.hubspot.jinjava.lib.tag.eager.DeferredToken;
15+
import com.hubspot.jinjava.tree.parse.ExpressionToken;
816
import java.io.IOException;
917
import java.nio.charset.StandardCharsets;
18+
import java.util.Collections;
19+
import java.util.concurrent.atomic.AtomicInteger;
1020
import org.junit.Before;
1121
import org.junit.Test;
1222

@@ -54,6 +64,59 @@ public void itDoesntChopWordsWhenSpecified() {
5464
);
5565
}
5666

67+
@Test
68+
public void itExecutesJinjavaInsideTag() {
69+
assertThat(
70+
filter.filter("{% for i in [1, 2, 3] %}<div>{{i}}</div>{% endfor %}", interpreter)
71+
)
72+
.isEqualTo("<div>\n 1\n</div>\n<div>\n 2\n</div>\n<div>\n 3\n</div>");
73+
}
74+
75+
@Test
76+
public void itExecutesJinjavaInsideTagAndTruncates() {
77+
assertThat(
78+
filter.filter(
79+
"{% for i in [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12] %}<div>{{i}}</div>{% endfor %}",
80+
interpreter,
81+
"10"
82+
)
83+
)
84+
.isEqualTo(
85+
"<div>\n 1\n</div>\n<div>\n 2\n</div>\n<div>\n 3\n</div>\n<div>\n 4\n</div>\n<div>\n 5\n</div>\n" +
86+
"<div>\n 6\n</div>\n<div>\n 7\n</div>\n<div>\n 8\n</div>\n<div>\n 9\n</div>\n<div>\n ...\n</div>"
87+
);
88+
}
89+
90+
@Test
91+
public void itIsolatesJinjavaScopeWhenExecutingCodeInsideTag() {
92+
filter.filter("{% set test = 'hello' %}", interpreter);
93+
assertThat(interpreter.getContext().get("test")).isNull();
94+
}
95+
96+
@Test
97+
public void itThrowsDeferredValueExceptionWhenDeferredTokensAreLeft() {
98+
AtomicInteger counter = new AtomicInteger();
99+
JinjavaInterpreter mockedInterpreter = mock(JinjavaInterpreter.class);
100+
Context mockedContext = mock(Context.class);
101+
when(mockedInterpreter.getContext()).thenReturn(mockedContext);
102+
when(mockedContext.getDeferredTokens())
103+
.thenAnswer(i ->
104+
counter.getAndIncrement() == 0
105+
? Collections.emptySet()
106+
: Collections.singleton(
107+
DeferredToken
108+
.builderFromImage(
109+
"{{ deferred && other }}",
110+
ExpressionToken.class,
111+
interpreter
112+
)
113+
.build()
114+
)
115+
);
116+
assertThatThrownBy(() -> filter.filter("{{ deferred && other }}", mockedInterpreter))
117+
.isInstanceOf(DeferredValueException.class);
118+
}
119+
57120
@Test
58121
public void itTakesKwargs() {
59122
String result = (String) filter.filter(

0 commit comments

Comments
 (0)