001// Generated by delombok at Sun Jul 30 17:21:56 UTC 2023 002package de.cuioss.test.jsf.renderer; 003 004import static de.cuioss.tools.string.MoreStrings.emptyToNull; 005import static java.util.Objects.requireNonNull; 006import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 007import static org.junit.jupiter.api.Assertions.assertNotNull; 008import static org.junit.jupiter.api.Assertions.assertNull; 009import static org.junit.jupiter.api.Assertions.assertThrows; 010import java.io.IOException; 011import java.io.StringWriter; 012import javax.faces.component.UIComponent; 013import javax.faces.context.FacesContext; 014import javax.faces.render.FacesRenderer; 015import javax.faces.render.Renderer; 016import org.apache.myfaces.test.mock.MockResponseWriter; 017import org.jdom2.Document; 018import org.junit.jupiter.api.BeforeEach; 019import org.junit.jupiter.api.Test; 020import de.cuioss.test.jsf.junit5.EnableJsfEnvironment; 021import de.cuioss.test.jsf.junit5.JsfEnabledTestEnvironment; 022import de.cuioss.test.jsf.renderer.util.DomUtils; 023import de.cuioss.test.jsf.renderer.util.HtmlTreeAsserts; 024import de.cuioss.test.valueobjects.objects.ConfigurationCallBackHandler; 025import de.cuioss.test.valueobjects.objects.impl.DefaultInstantiator; 026import de.cuioss.tools.reflect.MoreReflection; 027import de.cuioss.tools.string.MoreStrings; 028 029/** 030 * Base class for testing implementations of {@link Renderer}. It focuses on 031 * conveniences and the basic-api contracts. 032 * <h3>Configuration</h3> 033 * <p> 034 * Documentation on the setup of the JSF-related test-infrastructure can be 035 * found at {@link EnableJsfEnvironment} 036 * </p> 037 * <p> 038 * It acts as an {@link ConfigurationCallBackHandler}, saying after 039 * initialization and prior to testing the method {@link #configure(Object)} 040 * will be called allowing the concrete test-class to do some specific 041 * configuration e.g. calling init-methods and such. 042 * </p> 043 * <p> 044 * You can easily access pre-configured instance by calling 045 * {@link #getRenderer()}. 046 * </p> 047 * <h3>API-Tests</h3> Base {@linkplain Renderer} Test. Verify API contract of 048 * Renderer for 049 * <ul> 050 * <li>{@linkplain Renderer#decode(FacesContext, UIComponent)}</li> 051 * <li>{@linkplain Renderer#encodeBegin(FacesContext, UIComponent)}</li> 052 * <li>{@linkplain Renderer#encodeChildren(FacesContext, UIComponent)}</li> 053 * <li>{@linkplain Renderer#encodeEnd(FacesContext, UIComponent)}</li> 054 * <li>{@linkplain Renderer#convertClientId(FacesContext, String)}</li> 055 * </ul> 056 * <h3>Contracts</h3> 057 * <ul> 058 * <li>{@link #assertRenderResult(UIComponent, Document)} and 059 * {@link #assertRenderResult(UIComponent, String)} are the main 'business' 060 * methods for explicit testing</li> 061 * </ul> 062 * 063 * @author Oliver Wolff 064 * @param <R> The renderer being tested 065 */ 066public abstract class AbstractRendererTestBase<R extends Renderer> extends JsfEnabledTestEnvironment implements ConfigurationCallBackHandler<R> { 067 private static final String NPE_ON_MISSING_CLIENT_ID_EXPECTED = "NullPointerException expected on missing ClientId parameter. Use inheritance or implement own check."; 068 private static final String NPE_ON_MISSING_PARAMETER_EXPECTED = "NullPointerException expected on missing UIComponent parameter. Use inheritance or implement own check."; 069 private static final String NPE_ON_MISSING_FACESCONTEXT_EXPECTED = "NullPointerException expected on missing FacesContext. Use inheritance or implement own check."; 070 private R renderer; 071 072 /** 073 * Instantiates and initially configures a concrete {@link Renderer} 074 */ 075 @BeforeEach 076 void initRenderer() { 077 final Class<R> klazz = MoreReflection.extractFirstGenericTypeArgument(getClass()); 078 renderer = new DefaultInstantiator<>(klazz).newInstance(); 079 configure(renderer); 080 if (klazz.isAnnotationPresent(FacesRenderer.class)) { 081 getComponentConfigDecorator().registerRenderer(klazz); 082 } 083 } 084 085 /** 086 * @return the configured {@link UIComponent}. <em>Caution: </em> you must 087 * always create a new instance of the component on each call 088 */ 089 protected abstract UIComponent getComponent(); 090 091 /** 092 * Renders the given component / renderer into a String representation 093 * 094 * @param toBeRendered the component to be passed to the renderer, must not be 095 * null 096 * @return the String-result of the rendering 097 * @throws IOException 098 */ 099 public String renderToString(final UIComponent toBeRendered) throws IOException { 100 requireNonNull(toBeRendered); 101 var output = new StringWriter(); 102 getFacesContext().setResponseWriter(new MockResponseWriter(output)); 103 final Renderer testRenderer = getRenderer(); 104 testRenderer.encodeBegin(getFacesContext(), toBeRendered); 105 testRenderer.encodeChildren(getFacesContext(), toBeRendered); 106 testRenderer.encodeEnd(getFacesContext(), toBeRendered); 107 return output.toString(); 108 } 109 110 /** 111 * Calls the renderer and checks the result against the given expected 112 * {@link Document} 113 * 114 * @param toBeRendered the component to be passed to the renderer, must not be 115 * null 116 * @param expected must not be null 117 */ 118 public void assertRenderResult(final UIComponent toBeRendered, final Document expected) { 119 var rendered = assertDoesNotThrow(() -> renderToString(toBeRendered)); 120 assertNotNull(emptyToNull(rendered), "Render result must not be empty."); 121 HtmlTreeAsserts.assertHtmlTreeEquals(expected, DomUtils.htmlStringToDocument(rendered)); 122 } 123 124 /** 125 * Shorthand for {@link #assertRenderResult(UIComponent, Document)} and 126 * {@link DomUtils#htmlStringToDocument(String)} 127 * 128 * @param toBeRendered the component to be passed to the renderer, must not be 129 * null 130 * @param expected must not be null 131 */ 132 public void assertRenderResult(final UIComponent toBeRendered, final String expected) { 133 assertNotNull(emptyToNull(expected), "Render result must not be empty."); 134 assertRenderResult(toBeRendered, DomUtils.htmlStringToDocument(expected)); 135 } 136 137 /** 138 * Assert, that the given component does not render any output. 139 * 140 * @param toBeRendered the component to be passed to the renderer, must not be 141 * null 142 */ 143 public void assertEmptyRenderResult(final UIComponent toBeRendered) { 144 var rendered = assertDoesNotThrow(() -> renderToString(toBeRendered)); 145 assertNull(MoreStrings.emptyToNull(rendered), "Render result must be empty, but is:\n" + rendered); 146 } 147 148 // API tests 149 @Test 150 void shouldThrowNPEOnMissingParameterForDecode() { 151 assertThrows(NullPointerException.class, () -> renderer.decode(null, getComponent()), NPE_ON_MISSING_FACESCONTEXT_EXPECTED); 152 assertThrows(NullPointerException.class, () -> renderer.decode(getFacesContext(), null), NPE_ON_MISSING_PARAMETER_EXPECTED); 153 } 154 155 @Test 156 void shouldThrowNPEOnMissingParameterForEncodeBegin() { 157 assertThrows(NullPointerException.class, () -> renderer.encodeBegin(null, getComponent()), NPE_ON_MISSING_FACESCONTEXT_EXPECTED); 158 assertThrows(NullPointerException.class, () -> renderer.encodeBegin(getFacesContext(), null)); 159 } 160 161 @Test 162 void shouldThrowNPEOnMissingParameterForEncodeChildren() { 163 assertThrows(NullPointerException.class, () -> renderer.encodeChildren(null, getComponent()), NPE_ON_MISSING_FACESCONTEXT_EXPECTED); 164 assertThrows(NullPointerException.class, () -> renderer.encodeChildren(getFacesContext(), null), NPE_ON_MISSING_PARAMETER_EXPECTED); 165 } 166 167 @Test 168 void shouldThrowNPEOnMissingParameterForConvertClientId() { 169 assertThrows(NullPointerException.class, () -> renderer.convertClientId(null, "SomeId"), NPE_ON_MISSING_FACESCONTEXT_EXPECTED); 170 assertThrows(NullPointerException.class, () -> renderer.convertClientId(getFacesContext(), null), NPE_ON_MISSING_CLIENT_ID_EXPECTED); 171 } 172 173 @Test 174 void shouldThrowNPEOnMissingParameterForEncodeEnd() { 175 assertThrows(NullPointerException.class, () -> renderer.encodeEnd(null, getComponent()), NPE_ON_MISSING_FACESCONTEXT_EXPECTED); 176 assertThrows(NullPointerException.class, () -> renderer.encodeEnd(getFacesContext(), null), NPE_ON_MISSING_PARAMETER_EXPECTED); 177 } 178 179 @java.lang.SuppressWarnings("all") 180 @lombok.Generated 181 public R getRenderer() { 182 return this.renderer; 183 } 184}