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.collect.CollectionLiterals.mutableList; 005import static org.junit.jupiter.api.Assertions.assertDoesNotThrow; 006import java.util.ArrayList; 007import java.util.Arrays; 008import java.util.Collection; 009import java.util.HashSet; 010import java.util.List; 011import java.util.Optional; 012import java.util.Set; 013import javax.faces.component.UIComponent; 014import javax.faces.component.UIViewRoot; 015import javax.faces.component.html.HtmlForm; 016import javax.faces.event.FacesEvent; 017import javax.faces.event.PostAddToViewEvent; 018import javax.faces.render.Renderer; 019import org.junit.jupiter.api.BeforeEach; 020import org.junit.jupiter.api.Test; 021import de.cuioss.test.jsf.config.renderer.VerifyComponentRendererConfig; 022import de.cuioss.test.jsf.config.renderer.VetoRenderAttributeAssert; 023import de.cuioss.test.jsf.renderer.util.DomUtils; 024import de.cuioss.tools.reflect.FieldWrapper; 025import de.cuioss.tools.reflect.MoreReflection; 026 027/** 028 * While {@link AbstractRendererTestBase} focuses on API-Contract utility 029 * methods, this class provides a number of implicit tests. Simplifying the 030 * testing of concrete renderer behavior. 031 * <h3>Configuration</h3> You can configure this test by 032 * <ul> 033 * <li>{@link VerifyComponentRendererConfig}</li> 034 * <li>{@link VetoRenderAttributeAssert}</li> 035 * </ul> 036 * 037 * @author Oliver Wolff 038 * @param <R> The renderer being tested 039 */ 040public abstract class AbstractComponentRendererTest<R extends Renderer> extends AbstractRendererTestBase<R> { 041 private Set<RendererAttributeAssert> activeAsserts; 042 private boolean wrapComponentInForm = false; 043 044 /** 045 * Initializes the active {@link RendererAttributeAssert} and checks for the 046 * {@link VerifyComponentRendererConfig} annotation 047 */ 048 @BeforeEach 049 public void initAbstractComponentRendererTest() { 050 handleRenderAttributeAsserts(); 051 handleConfigAnnotation(); 052 } 053 054 private void handleConfigAnnotation() { 055 Optional<VerifyComponentRendererConfig> configOption = MoreReflection.extractAnnotation(getClass(), VerifyComponentRendererConfig.class); 056 configOption.ifPresent(verifyComponentRendererConfig -> wrapComponentInForm = verifyComponentRendererConfig.wrapComponentInForm()); 057 } 058 059 private void handleRenderAttributeAsserts() { 060 activeAsserts = new HashSet<>(Arrays.asList(CommonRendererAsserts.values())); 061 final Set<CommonRendererAsserts> vetoes = new HashSet<>(); 062 MoreReflection.extractAllAnnotations(this.getClass(), VetoRenderAttributeAssert.class).forEach(veto -> vetoes.addAll(mutableList(veto.value()))); 063 activeAsserts.removeAll(vetoes); 064 } 065 066 /** 067 * Iterates through all active RendererAttributeAssert and tests them 068 * accordingly 069 */ 070 @Test 071 public void shouldHandleRendererAttributeAsserts() { 072 for (RendererAttributeAssert attributeAssert : activeAsserts) { 073 var component = getWrappedComponent(); 074 attributeAssert.applyAttribute(component); 075 component.processEvent(new PostAddToViewEvent(component)); 076 var document = DomUtils.htmlStringToDocument(assertDoesNotThrow(() -> super.renderToString(component))); 077 attributeAssert.assertAttributeSet(document.getRootElement()); 078 } 079 } 080 081 /** 082 * @return the {@link UIComponent} derived by {@link #getComponent()} or the 083 * same wrapped in an {@link HtmlForm} in case 084 * {@link #isWrapComponentInForm()} is {@code true} 085 */ 086 protected UIComponent getWrappedComponent() { 087 var component = getComponent(); 088 if (wrapComponentInForm) { 089 var form = new HtmlForm(); 090 form.getChildren().add(component); 091 } 092 return component; 093 } 094 095 // owolff: Sonar false-positive 096 /** 097 * Helper method that extracts all queued events from {@link UIViewRoot}. In 098 * order to use this method the corresponding test must ensure, that the 099 * component under test is child of {@link UIViewRoot}. Otherwise the events can 100 * not be extracted 101 * 102 * @return the plain list of events available at {@link UIViewRoot} at this 103 * time. 104 */ 105 @SuppressWarnings({"unchecked"}) 106 public List<FacesEvent> extractEventsFromViewRoot() { 107 final List<FacesEvent> found = new ArrayList<>(); 108 final var uiViewRoot = getFacesContext().getViewRoot(); 109 // Hacky: Private field of myfaces 110 var eventField = FieldWrapper.from(UIViewRoot.class, "_events"); 111 if (eventField.isPresent()) { 112 var events = eventField.get().readValue(uiViewRoot); 113 events.ifPresent(o -> found.addAll((Collection<? extends FacesEvent>) o)); 114 return found; 115 } 116 // Hacky: Private field of mojarra 117 eventField = FieldWrapper.from(UIViewRoot.class, "events"); 118 if (eventField.isEmpty()) { 119 throw new AssertionError("javax.faces.component.UIViewRoot provides neither the field \'events\' nor \'_events\'"); 120 } 121 var events = eventField.get().readValue(uiViewRoot); 122 if (events.isPresent()) { 123 var eventLists = (List<List<FacesEvent>>) events.get(); 124 eventLists.forEach(found::addAll); 125 } 126 return found; 127 } 128 129 @java.lang.SuppressWarnings("all") 130 @lombok.Generated 131 public Set<RendererAttributeAssert> getActiveAsserts() { 132 return this.activeAsserts; 133 } 134 135 @java.lang.SuppressWarnings("all") 136 @lombok.Generated 137 public boolean isWrapComponentInForm() { 138 return this.wrapComponentInForm; 139 } 140}