001package de.cuioss.test.jsf.junit5;
002
003import static de.cuioss.test.jsf.util.ConfigurationHelper.configureApplication;
004import static de.cuioss.test.jsf.util.ConfigurationHelper.configureComponents;
005import static de.cuioss.test.jsf.util.ConfigurationHelper.configureManagedBeans;
006import static de.cuioss.test.jsf.util.ConfigurationHelper.configureRequestConfig;
007
008import java.util.ArrayList;
009import java.util.LinkedHashSet;
010import java.util.List;
011import java.util.Objects;
012import java.util.Optional;
013import java.util.Set;
014import java.util.stream.Collectors;
015
016import org.apache.myfaces.test.mock.MockFacesContext;
017import org.junit.jupiter.api.extension.AfterEachCallback;
018import org.junit.jupiter.api.extension.ExtensionContext;
019import org.junit.jupiter.api.extension.ExtensionContext.Namespace;
020import org.junit.jupiter.api.extension.TestInstancePostProcessor;
021import org.junit.platform.commons.logging.Logger;
022import org.junit.platform.commons.logging.LoggerFactory;
023import org.junit.platform.commons.support.AnnotationSupport;
024
025import de.cuioss.test.jsf.config.JsfTestConfiguration;
026import de.cuioss.test.jsf.util.ConfigurableApplication;
027import de.cuioss.test.jsf.util.JsfEnvironmentConsumer;
028import de.cuioss.test.jsf.util.JsfEnvironmentHolder;
029import de.cuioss.test.jsf.util.JsfRuntimeSetup;
030
031/**
032 * Starts and Configures the {@link JsfRuntimeSetup}, wraps it into an
033 * {@link JsfEnvironmentHolder} and inject it into an
034 * {@link JsfEnvironmentConsumer} if available
035 *
036 * @author Oliver Wolff
037 *
038 */
039public class JsfSetupExtension implements TestInstancePostProcessor, AfterEachCallback {
040
041    private static final Logger LOGGER = LoggerFactory.getLogger(JsfSetupExtension.class);
042
043    /**
044     * Identifies the {@link Namespace} under which the concrete instance of
045     * {@link JsfRuntimeSetup} is stored .
046     */
047    public static final Namespace NAMESPACE = Namespace.create("test", "jsf", "JsfRuntimeSetup");
048
049    @Override
050    @SuppressWarnings("squid:S3655")
051    public void postProcessTestInstance(Object testInstance, ExtensionContext context) throws Exception {
052        var setup = new JsfRuntimeSetup();
053        LOGGER.info(() -> "Starting JSF-Environment");
054        setup.setUp();
055        put(setup, context);
056
057        var useIdentityResourceBundle = false;
058        List<EnableJsfEnvironment> environments = new ArrayList<>();
059        retrieveEnableJSFAnnotations(testInstance.getClass(), environments);
060        if (!environments.isEmpty()) {
061            // Use the outermost annotation
062            useIdentityResourceBundle = environments.get(0).useIdentityResourceBundle();
063        }
064
065        var environment = new JsfEnvironmentHolder(setup);
066        ConfigurableApplication.createWrapAndRegister((MockFacesContext) environment.getFacesContext())
067                .setUseIdentityResouceBundle(useIdentityResourceBundle);
068
069        // Ensure that the ConfigurableApplication is set for
070        // JsfRuntimeSetup#getApplication
071        setup.setApplication(environment.getFacesContext().getApplication());
072
073        LOGGER.debug(() -> "Registering Decorators");
074        Set<JsfTestConfiguration> decoratorAnnotations = new LinkedHashSet<>(16);
075
076        retrieveDecoratorAnnotations(testInstance.getClass(), decoratorAnnotations);
077        decoratorAnnotations = decoratorAnnotations.stream().filter(Objects::nonNull).collect(Collectors.toSet());
078
079        configureApplication(testInstance, environment.getApplicationConfigDecorator(), decoratorAnnotations);
080        configureComponents(testInstance, environment.getComponentConfigDecorator(), decoratorAnnotations);
081        configureManagedBeans(testInstance, environment.getBeanConfigDecorator(), decoratorAnnotations);
082        configureRequestConfig(testInstance, environment.getRequestConfigDecorator(), decoratorAnnotations);
083
084        if (testInstance instanceof JsfEnvironmentConsumer) {
085            ((JsfEnvironmentConsumer) testInstance).setEnvironmentHolder(environment);
086        }
087    }
088
089    private void retrieveDecoratorAnnotations(Class<?> type, Set<JsfTestConfiguration> result) {
090        if (null == type || Object.class.equals(type)) {
091            return;
092        }
093        result.addAll(AnnotationSupport.findRepeatableAnnotations(type, JsfTestConfiguration.class));
094        retrieveDecoratorAnnotations(type.getSuperclass(), result);
095    }
096
097    private void retrieveEnableJSFAnnotations(Class<?> type, List<EnableJsfEnvironment> result) {
098        if (null == type || Object.class.equals(type)) {
099            return;
100        }
101        result.addAll(AnnotationSupport.findRepeatableAnnotations(type, EnableJsfEnvironment.class));
102        retrieveEnableJSFAnnotations(type.getSuperclass(), result);
103    }
104
105    @Override
106    public void afterEach(ExtensionContext context) {
107        LOGGER.debug(() -> "Tear-Down JSF-Environment");
108        get(context).ifPresent(JsfRuntimeSetup::tearDown);
109    }
110
111    private static void put(JsfRuntimeSetup runtimeSetup, ExtensionContext context) {
112        context.getStore(NAMESPACE).put(JsfRuntimeSetup.class.getName(), runtimeSetup);
113    }
114
115    private Optional<JsfRuntimeSetup> get(ExtensionContext context) {
116        return Optional.ofNullable((JsfRuntimeSetup) context.getStore(NAMESPACE).get(JsfRuntimeSetup.class.getName()));
117    }
118}