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}