You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@myfaces.apache.org by bo...@apache.org on 2024/01/30 18:53:09 UTC
(myfaces-tobago) branch main updated: feat: Support for PassThroughAttributes in Tobago (#4801)
This is an automated email from the ASF dual-hosted git repository.
bommel pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/myfaces-tobago.git
The following commit(s) were added to refs/heads/main by this push:
new a91f06245d feat: Support for PassThroughAttributes in Tobago (#4801)
a91f06245d is described below
commit a91f06245da2ecafb0bb35fb058e924b2b7e46ad
Author: Bernd Bohmann <bo...@apache.org>
AuthorDate: Tue Jan 30 19:53:03 2024 +0100
feat: Support for PassThroughAttributes in Tobago (#4801)
issue: TOBAGO-2277
---
.../internal/renderkit/renderer/ImageRenderer.java | 2 +-
.../internal/renderkit/renderer/InRenderer.java | 2 +-
.../internal/renderkit/renderer/OutRenderer.java | 6 +-
.../renderkit/renderer/TextareaRenderer.java | 2 +-
.../webapp/DebugResponseWriterWrapper.java | 12 +-
.../tobago/internal/webapp/HtmlResponseWriter.java | 1 +
.../internal/webapp/TobagoResponseWriterBase.java | 154 ++++++++++++++++-----
.../webapp/TobagoResponseWriterWrapper.java | 5 +
.../tobago/internal/webapp/XmlResponseWriter.java | 1 +
.../tobago/webapp/TobagoResponseWriter.java | 2 +
.../internal/config/AbstractTobagoTestBase.java | 4 +
.../webapp/TobagoResponseWriterUnitTest.java | 103 +++++++++++++-
12 files changed, 252 insertions(+), 42 deletions(-)
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/ImageRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/ImageRenderer.java
index 153b542349..dddc040f53 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/ImageRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/ImageRenderer.java
@@ -66,7 +66,7 @@ public class ImageRenderer<T extends AbstractUIImage> extends RendererBase<T> {
disabled ? BootstrapClass.DISABLED : null,
component.getCustomClass());
writer.writeAttribute(HtmlAttributes.TITLE, title, true);
- writer.startElement(HtmlElements.IMG);
+ writer.startElement(HtmlElements.IMG, component);
writer.writeAttribute(HtmlAttributes.SRC, value, true);
writer.writeAttribute(HtmlAttributes.ALT, alt != null ? alt : "", true);
writer.endElement(HtmlElements.IMG);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java
index 7069ae7fd0..b4350ae667 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/InRenderer.java
@@ -98,7 +98,7 @@ public class InRenderer<T extends AbstractUIIn> extends DecorationPositionRender
}
encodeGroupAddon(facesContext, writer, before, false);
- writer.startElement(HtmlElements.INPUT);
+ writer.startElement(HtmlElements.INPUT, component);
if (component.getAccessKey() != null) {
writer.writeAttribute(HtmlAttributes.ACCESSKEY, Character.toString(component.getAccessKey()), false);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRenderer.java
index d7a2c76214..69a76a3398 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/OutRenderer.java
@@ -88,7 +88,8 @@ public class OutRenderer<T extends AbstractUIOut> extends DecorationPositionRend
final TobagoResponseWriter writer = getResponseWriter(facesContext);
final Markup markup = component.getMarkup() != null ? component.getMarkup() : Markup.NULL;
- writer.startElement(isInside(facesContext, HtmlElements.TOBAGO_IN) ? HtmlElements.TOBAGO_OUT : HtmlElements.SPAN);
+ boolean inside = isInside(facesContext, HtmlElements.TOBAGO_IN);
+ writer.startElement(inside ? HtmlElements.TOBAGO_OUT : HtmlElements.SPAN, component);
HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
writer.writeClassAttribute(
@@ -97,8 +98,7 @@ public class OutRenderer<T extends AbstractUIOut> extends DecorationPositionRend
markup.contains(Markup.LARGE) ? BootstrapClass.FORM_CONTROL_LG : null,
markup.contains(Markup.SMALL) ? BootstrapClass.FORM_CONTROL_SM : null,
component.getCustomClass(),
- isInside(facesContext, HtmlElements.TOBAGO_IN)
- ? BootstrapClass.INPUT_GROUP_TEXT : BootstrapClass.FORM_CONTROL_PLAINTEXT,
+ inside ? BootstrapClass.INPUT_GROUP_TEXT : BootstrapClass.FORM_CONTROL_PLAINTEXT,
BootstrapClass.textColor(markup),
BootstrapClass.fontStyle(markup));
final String title = HtmlRendererUtils.getTitleFromTipAndMessages(facesContext, component);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TextareaRenderer.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TextareaRenderer.java
index e62578f96c..f270772c1c 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TextareaRenderer.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/renderkit/renderer/TextareaRenderer.java
@@ -86,7 +86,7 @@ public class TextareaRenderer<T extends AbstractUITextarea> extends DecorationPo
final Markup markup = component.getMarkup() != null ? component.getMarkup() : Markup.NULL;
final TobagoResponseWriter writer = getResponseWriter(facesContext);
- writer.startElement(HtmlElements.TEXTAREA);
+ writer.startElement(HtmlElements.TEXTAREA, component);
writer.writeNameAttribute(clientId);
writer.writeIdAttribute(fieldId);
HtmlRendererUtils.writeDataAttributes(facesContext, writer, component);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/DebugResponseWriterWrapper.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/DebugResponseWriterWrapper.java
index b08fb4c4ae..89e55d8110 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/DebugResponseWriterWrapper.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/DebugResponseWriterWrapper.java
@@ -181,10 +181,20 @@ public class DebugResponseWriterWrapper extends TobagoResponseWriter {
usedAttributes.clear();
}
+ @Override
+ public void startElement(final HtmlElements name, UIComponent component) throws IOException {
+ if (LOG.isDebugEnabled()) {
+ LOG.debug("start element: '{}'", name);
+ }
+ stack.push(name);
+ responseWriter.startElement(name, component);
+
+ usedAttributes.clear();
+ }
@Override
public void startElement(final HtmlElements name) throws IOException {
if (LOG.isDebugEnabled()) {
- LOG.debug("start element: '" + name + "'");
+ LOG.debug("start element: '{}'", name);
}
stack.push(name);
responseWriter.startElement(name);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/HtmlResponseWriter.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/HtmlResponseWriter.java
index 468dd9048f..40d115b7e3 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/HtmlResponseWriter.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/HtmlResponseWriter.java
@@ -86,6 +86,7 @@ public class HtmlResponseWriter extends TobagoResponseWriterBase {
@Override
protected void closeEmptyTag() throws IOException {
+ handlePassThroughAttributes();
getWriter().write(">");
}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoResponseWriterBase.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoResponseWriterBase.java
index ebb0f234e9..6ce3e5932d 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoResponseWriterBase.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoResponseWriterBase.java
@@ -26,7 +26,10 @@ import org.apache.myfaces.tobago.webapp.TobagoResponseWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
+import jakarta.el.ValueExpression;
import jakarta.faces.component.UIComponent;
+import jakarta.faces.context.FacesContext;
+import jakarta.faces.render.Renderer;
import java.io.IOException;
import java.io.Writer;
@@ -34,6 +37,9 @@ import java.lang.invoke.MethodHandles;
import java.net.URI;
import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
+import java.util.HashMap;
+import java.util.Locale;
+import java.util.Map;
public abstract class TobagoResponseWriterBase extends TobagoResponseWriter {
@@ -48,6 +54,7 @@ public abstract class TobagoResponseWriterBase extends TobagoResponseWriter {
private int inlineStack = 0;
private UIComponent component;
+ private Map<Integer, String> levelToChangedElementTag = new HashMap<>();
private boolean startStillOpen;
@@ -171,11 +178,52 @@ public abstract class TobagoResponseWriterBase extends TobagoResponseWriter {
protected void closeOpenTag() throws IOException {
if (startStillOpen) {
+ handlePassThroughAttributes();
writer.write('>');
startStillOpen = false;
}
}
+ protected void handlePassThroughAttributes() throws IOException {
+ if (component != null && component.getPassThroughAttributes(false) != null) {
+ for (Map.Entry<String, Object> entry : component.getPassThroughAttributes().entrySet()) {
+ String key = entry.getKey();
+ if (Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY.equals(key)) {
+ // skip rendering
+ continue;
+ }
+
+ Object value = entry.getValue();
+ if (value instanceof ValueExpression) {
+ value = ((ValueExpression) value).getValue(FacesContext.getCurrentInstance().getELContext());
+ }
+ // Faces 2.2 In the renderkit javadoc of jsf 2.2 spec says this
+ // (Rendering Pass Through Attributes):
+ // "... The ResponseWriter must ensure that any pass through attributes are
+ // rendered on the outer-most markup element for the component. If there is
+ // a pass through attribute with the same name as a renderer specific
+ // attribute, the pass through attribute takes precedence. Pass through
+ // attributes are rendered as if they were passed to
+ // ResponseWriter.writeURIAttribute(). ..."
+ // Note here it says "as if they were passed", instead say "... attributes are
+ // encoded and rendered as if ...". Black box testing against RI shows that there
+ // is no URI encoding at all in this part, so in this case the best is do the
+ // same here. After all, it is resposibility of the one who set the passthrough
+ // attribute to do the proper encoding in cases when a URI is provided. However,
+ // that does not means the attribute should not be encoded as other attributes.
+ // TODO boolean should be handled in writeAttributeInternal
+ if (value instanceof Boolean) {
+ if (!Boolean.FALSE.equals(value)) {
+ writer.write(' ');
+ writer.write(entry.getKey());
+ }
+ } else {
+ writeAttributeInternal(writer, entry.getKey(), value.toString(), true);
+ }
+ }
+ }
+ }
+
@Override
public void startDocument() throws IOException {
// nothing to do
@@ -199,27 +247,41 @@ public abstract class TobagoResponseWriterBase extends TobagoResponseWriter {
@Override
public void startElement(final String name, final UIComponent currentComponent) throws IOException {
final boolean inline = HtmlElements.isInline(name);
- if (inline) {
- inlineStack++;
- }
this.component = currentComponent;
- startElementInternal(writer, name, HtmlElements.isInline(name));
+ startElementInternal(writer, name, inline);
+ }
+
+ public void startElement(final HtmlElements name, final UIComponent currentComponent) throws IOException {
+ this.component = currentComponent;
+ startElement(name);
}
@Override
public void startElement(final HtmlElements name) throws IOException {
- final boolean inline = name.isInline();
- if (inline) {
- inlineStack++;
- }
startElementInternal(writer, name.getValue(), name.isInline());
- if (!name.isVoid()) {
- level++;
- }
}
protected void startElementInternal(final Writer sink, final String name, final boolean inline)
throws IOException {
+ String localElementName = null;
+ boolean localElementInline = inline;
+ if (component != null && component.getPassThroughAttributes(false) != null) {
+ Map<String, Object> passThroughAttributes = component.getPassThroughAttributes(false);
+ Object value = passThroughAttributes.get(Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY);
+ if (value != null) {
+ if (value instanceof ValueExpression) {
+ value = ((ValueExpression) value).getValue(FacesContext.getCurrentInstance().getELContext());
+ }
+ localElementName = value.toString().toLowerCase(Locale.ROOT).trim();
+ if (!name.equals(localElementName)) {
+ levelToChangedElementTag.put(level, localElementName);
+ localElementInline = HtmlElements.isInline(localElementName);
+ }
+ }
+ }
+ if (localElementInline) {
+ inlineStack++;
+ }
if (startStillOpen) {
sink.write('>');
}
@@ -230,17 +292,31 @@ public abstract class TobagoResponseWriterBase extends TobagoResponseWriter {
}
}
sink.write('<');
- sink.write(name);
+ if (localElementName != null) {
+ sink.write(localElementName);
+ } else {
+ sink.write(name);
+ }
startStillOpen = true;
+ level++;
}
@Override
public void endElement(final String name) throws IOException {
- final boolean inline = HtmlElements.isInline(name);
- if (HtmlElements.isVoid(name)) {
+ level--;
+ String elementTagToRender = levelToChangedElementTag.get(level);
+ if (elementTagToRender == null) {
+ elementTagToRender = name;
+ }
+ renderNonTobagoEndElement(elementTagToRender);
+ }
+
+ private void renderNonTobagoEndElement(String element) throws IOException {
+ final boolean inline = HtmlElements.isInline(element);
+ if (HtmlElements.isVoid(element)) {
closeEmptyTag();
} else {
- endElementInternal(writer, name, inline);
+ endElementInternal(writer, element, inline);
}
startStillOpen = false;
if (inline) {
@@ -251,19 +327,22 @@ public abstract class TobagoResponseWriterBase extends TobagoResponseWriter {
@Override
public void endElement(final HtmlElements name) throws IOException {
- final boolean inline = name.isInline();
- if (name.isVoid()) {
- closeEmptyTag();
+ level--;
+ String elementTagToRender = levelToChangedElementTag.remove(level);
+ if (elementTagToRender != null) {
+ renderNonTobagoEndElement(elementTagToRender);
} else {
- if (!name.isVoid()) {
- level--;
+ final boolean inline = name.isInline();
+ if (name.isVoid()) {
+ closeEmptyTag();
+ } else {
+ endElementInternal(writer, name.getValue(), inline);
+ }
+ startStillOpen = false;
+ if (inline) {
+ inlineStack--;
+ assert inlineStack >= 0;
}
- endElementInternal(writer, name.getValue(), inline);
- }
- startStillOpen = false;
- if (inline) {
- inlineStack--;
- assert inlineStack >= 0;
}
}
@@ -289,12 +368,7 @@ public abstract class TobagoResponseWriterBase extends TobagoResponseWriter {
throws IOException {
final String attribute = findValue(value, property);
- writeAttribute(new MarkupLanguageAttributes() {
- @Override
- public String getValue() {
- return name;
- }
- }, attribute, true);
+ writeAttributeInternal(writer, name, attribute, true);
}
protected final String getCallingClassStackTraceElementString() {
@@ -339,6 +413,7 @@ public abstract class TobagoResponseWriterBase extends TobagoResponseWriter {
protected void endElementInternal(final Writer sink, final String name, final boolean inline) throws IOException {
if (startStillOpen) {
+ handlePassThroughAttributes();
sink.write('>');
}
if (inline) {
@@ -368,10 +443,23 @@ public abstract class TobagoResponseWriterBase extends TobagoResponseWriter {
LOG.error(error);
throw new IllegalStateException(error);
}
+ // If there is a pass through attribute with the same
+ // name as a renderer specific attribute,
+ // the pass through attribute takes precedence.
+ String attributeName = name.getValue();
+ if (component != null && component.getPassThroughAttributes(false) != null
+ && component.getPassThroughAttributes(false).containsKey(attributeName)) {
+ return;
+ }
+ writeAttributeInternal(sink, attributeName, value, escape);
+ }
+ private void writeAttributeInternal(
+ final Writer sink, final String attributeName, final String value, final boolean escape)
+ throws IOException {
if (value != null) {
sink.write(' ');
- sink.write(name.getValue());
+ sink.write(attributeName);
sink.write("='");
writerAttributeValue(value, escape);
sink.write('\'');
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoResponseWriterWrapper.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoResponseWriterWrapper.java
index 0aeb1739f6..a41aa8ac2a 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoResponseWriterWrapper.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/TobagoResponseWriterWrapper.java
@@ -43,6 +43,11 @@ public class TobagoResponseWriterWrapper extends TobagoResponseWriter {
responseWriter.startElement(name, component);
}
+ @Override
+ public void startElement(final HtmlElements name, final UIComponent component) throws IOException {
+ responseWriter.startElement(name.getValue(), component);
+ }
+
@Override
public void startElement(final HtmlElements name) throws IOException {
responseWriter.startElement(name.getValue(), null);
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/XmlResponseWriter.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/XmlResponseWriter.java
index fab061425f..1fe50f8341 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/XmlResponseWriter.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/internal/webapp/XmlResponseWriter.java
@@ -66,6 +66,7 @@ public final class XmlResponseWriter extends TobagoResponseWriterBase {
@Override
protected void closeEmptyTag() throws IOException {
+ handlePassThroughAttributes();
getWriter().write("/>");
}
diff --git a/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriter.java b/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriter.java
index f26cb9d898..7d0bb00453 100644
--- a/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriter.java
+++ b/tobago-core/src/main/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriter.java
@@ -48,6 +48,8 @@ public abstract class TobagoResponseWriter extends ResponseWriter {
@Override
public abstract void startElement(String name, UIComponent component) throws IOException;
+ public abstract void startElement(HtmlElements name, UIComponent component) throws IOException;
+
public abstract void startElement(HtmlElements name) throws IOException;
/**
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
index a7d89d1fbf..385c9b1132 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/internal/config/AbstractTobagoTestBase.java
@@ -39,6 +39,7 @@ import org.apache.myfaces.tobago.component.UIColumn;
import org.apache.myfaces.tobago.component.UIDate;
import org.apache.myfaces.tobago.component.UIFile;
import org.apache.myfaces.tobago.component.UIGridLayout;
+import org.apache.myfaces.tobago.component.UIImage;
import org.apache.myfaces.tobago.component.UIIn;
import org.apache.myfaces.tobago.component.UILink;
import org.apache.myfaces.tobago.component.UILinks;
@@ -82,6 +83,7 @@ import org.apache.myfaces.tobago.internal.renderkit.renderer.ColumnRenderer;
import org.apache.myfaces.tobago.internal.renderkit.renderer.DateRenderer;
import org.apache.myfaces.tobago.internal.renderkit.renderer.FileRenderer;
import org.apache.myfaces.tobago.internal.renderkit.renderer.GridLayoutRenderer;
+import org.apache.myfaces.tobago.internal.renderkit.renderer.ImageRenderer;
import org.apache.myfaces.tobago.internal.renderkit.renderer.InRenderer;
import org.apache.myfaces.tobago.internal.renderkit.renderer.LinkRenderer;
import org.apache.myfaces.tobago.internal.renderkit.renderer.LinksRenderer;
@@ -173,6 +175,7 @@ public abstract class AbstractTobagoTestBase extends AbstractJsfTestCase {
application.addComponent(Tags.column.componentType(), UIColumn.class.getName());
application.addComponent(Tags.file.componentType(), UIFile.class.getName());
application.addComponent(Tags.gridLayout.componentType(), UIGridLayout.class.getName());
+ application.addComponent(Tags.image.componentType(), UIImage.class.getName());
application.addComponent(Tags.in.componentType(), UIIn.class.getName());
application.addComponent(Tags.link.componentType(), UILink.class.getName());
application.addComponent(Tags.links.componentType(), UILinks.class.getName());
@@ -223,6 +226,7 @@ public abstract class AbstractTobagoTestBase extends AbstractJsfTestCase {
renderKit.addRenderer(UIFile.COMPONENT_FAMILY, RendererTypes.FILE, new FileRenderer<UIFile>());
renderKit.addRenderer(UIGridLayout.COMPONENT_FAMILY, RendererTypes.GRID_LAYOUT,
new GridLayoutRenderer<UIGridLayout>());
+ renderKit.addRenderer(UIImage.COMPONENT_FAMILY, RendererTypes.IMAGE, new ImageRenderer<UIImage>());
renderKit.addRenderer(UIIn.COMPONENT_FAMILY, RendererTypes.IN, new InRenderer<UIIn>());
renderKit.addRenderer(UILink.COMPONENT_FAMILY, RendererTypes.LINK, new LinkRenderer<UILink>());
renderKit.addRenderer(UILinks.COMPONENT_FAMILY, RendererTypes.LINKS, new LinksRenderer<UILinks>());
diff --git a/tobago-core/src/test/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriterUnitTest.java b/tobago-core/src/test/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriterUnitTest.java
index cf59ed8b96..4c01ddcc2c 100644
--- a/tobago-core/src/test/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriterUnitTest.java
+++ b/tobago-core/src/test/java/org/apache/myfaces/tobago/webapp/TobagoResponseWriterUnitTest.java
@@ -19,11 +19,19 @@
package org.apache.myfaces.tobago.webapp;
-import org.apache.myfaces.test.base.junit.AbstractJsfTestCase;
+import jakarta.faces.render.Renderer;
+import org.apache.myfaces.tobago.component.RendererTypes;
+import org.apache.myfaces.tobago.component.Tags;
+import org.apache.myfaces.tobago.component.UIImage;
+import org.apache.myfaces.tobago.component.UIIn;
+import org.apache.myfaces.tobago.component.UIOut;
+import org.apache.myfaces.tobago.component.UITextarea;
+import org.apache.myfaces.tobago.internal.config.AbstractTobagoTestBase;
import org.apache.myfaces.tobago.internal.webapp.HtmlResponseWriter;
import org.apache.myfaces.tobago.internal.webapp.XmlResponseWriter;
import org.apache.myfaces.tobago.renderkit.html.HtmlAttributes;
import org.apache.myfaces.tobago.renderkit.html.HtmlElements;
+import org.apache.myfaces.tobago.util.ComponentUtils;
import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
@@ -32,7 +40,7 @@ import java.io.IOException;
import java.io.StringWriter;
import java.nio.charset.StandardCharsets;
-public class TobagoResponseWriterUnitTest extends AbstractJsfTestCase {
+public class TobagoResponseWriterUnitTest extends AbstractTobagoTestBase {
private StringWriter stringWriter;
private TobagoResponseWriter writer;
@@ -152,4 +160,95 @@ public class TobagoResponseWriterUnitTest extends AbstractJsfTestCase {
xmlResponseWriter.writeText("123".toCharArray(), 0, 3);
Assertions.assertEquals("123", stringWriter.toString());
}
+
+ @Test
+ public void testPassthrough() throws IOException {
+ final UIIn c = (UIIn) ComponentUtils.createComponent(
+ facesContext, Tags.in.componentType(), RendererTypes.In, "id");
+ c.getPassThroughAttributes().put("step", 1);
+ c.getPassThroughAttributes().put("type", "number");
+ writer.startElement(HtmlElements.INPUT.getValue(), c);
+ writer.writeAttribute(HtmlAttributes.VALUE.getValue(), "100", null);
+ writer.endElement(HtmlElements.INPUT.getValue());
+
+ Assertions.assertEquals("\n<input value='100' step='1' type='number'>",
+ stringWriter.toString());
+ }
+
+ @Test
+ public void testPassthroughChangeElementTag() throws IOException {
+ final UIIn c = (UIIn) ComponentUtils.createComponent(
+ facesContext, Tags.in.componentType(), RendererTypes.In, "id");
+ c.getPassThroughAttributes().put("step", 1);
+ c.getPassThroughAttributes().put(Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY, HtmlElements.TEXTAREA.getValue());
+ writer.startElement(HtmlElements.INPUT.getValue(), c);
+ writer.writeAttribute(HtmlAttributes.VALUE.getValue(), "100", null);
+ writer.endElement(HtmlElements.INPUT.getValue());
+
+ Assertions.assertEquals("\n<textarea value='100' step='1'></textarea>",
+ stringWriter.toString());
+ }
+
+ @Test
+ public void testPassthroughInRenderer() throws IOException {
+ final UIIn c = (UIIn) ComponentUtils.createComponent(
+ facesContext, Tags.in.componentType(), RendererTypes.In, "id");
+
+ c.getPassThroughAttributes().put("step", 1);
+ c.getPassThroughAttributes().put("type", "number");
+ c.encodeAll(facesContext);
+ Assertions.assertEquals("\n<tobago-in id='id' class='tobago-auto-spacing'>\n"
+ + " <input name='id' id='id::field' class='form-control' step='1' type='number'>\n"
+ + "</tobago-in>", getLastWritten());
+ }
+
+ @Test
+ public void testPassthroughChangeElementTagInRender() throws IOException {
+ final UIIn c = (UIIn) ComponentUtils.createComponent(
+ facesContext, Tags.in.componentType(), RendererTypes.In, "id");
+
+ c.getPassThroughAttributes().put("test", 1);
+ c.getPassThroughAttributes().put(Renderer.PASSTHROUGH_RENDERER_LOCALNAME_KEY, HtmlElements.TEXTAREA.getValue());
+ c.encodeAll(facesContext);
+ Assertions.assertEquals("\n<tobago-in id='id' class='tobago-auto-spacing'>\n"
+ + " <textarea type='text' name='id' id='id::field' class='form-control' test='1'></textarea>\n"
+ + "</tobago-in>", getLastWritten());
+
+ }
+
+ @Test
+ public void testPassthroughWithTextareaRenderer() throws IOException {
+ final UITextarea c = (UITextarea) ComponentUtils.createComponent(
+ facesContext, Tags.textarea.componentType(), RendererTypes.Textarea, "id");
+
+ c.getPassThroughAttributes().put("spellcheck", true);
+ c.encodeAll(facesContext);
+ Assertions.assertEquals("\n<tobago-textarea id='id' class='tobago-auto-spacing'>\n"
+ + " <textarea name='id' id='id::field' class='form-control' spellcheck></textarea>\n"
+ + "</tobago-textarea>", getLastWritten());
+ }
+
+ @Test
+ public void testPassthroughWithImgRenderer() throws IOException {
+ final UIImage c = (UIImage) ComponentUtils.createComponent(
+ facesContext, Tags.image.componentType(), RendererTypes.Image, "id");
+
+ c.getPassThroughAttributes().put("spellcheck", true);
+ c.encodeAll(facesContext);
+ Assertions.assertEquals("\n<tobago-image id='id'>\n"
+ + " <img alt='' spellcheck>\n"
+ + "</tobago-image>", getLastWritten());
+ }
+
+ @Test
+ public void testPassthroughWithOutRenderer() throws IOException {
+ final UIOut c = (UIOut) ComponentUtils.createComponent(
+ facesContext, Tags.out.componentType(), RendererTypes.Out, "id");
+
+ c.getPassThroughAttributes().put("spellcheck", true);
+ c.encodeAll(facesContext);
+ Assertions.assertEquals("\n<tobago-out id='id' class='tobago-auto-spacing'>"
+ + "<span class='form-control-plaintext' spellcheck></span>"
+ + "</tobago-out>", getLastWritten());
+ }
}