Configuration.java

package io.github.giulong.spectrum.utils;

import com.fasterxml.jackson.annotation.JacksonInject;
import com.fasterxml.jackson.annotation.JsonIgnore;
import com.fasterxml.jackson.annotation.JsonPropertyDescription;
import com.fasterxml.jackson.databind.annotation.JsonSerialize;
import com.fasterxml.jackson.databind.ser.std.ToStringSerializer;
import io.github.giulong.spectrum.drivers.Driver;
import io.github.giulong.spectrum.interfaces.JsonSchemaTypes;
import io.github.giulong.spectrum.utils.environments.Environment;
import io.github.giulong.spectrum.utils.events.EventsConsumer;
import io.github.giulong.spectrum.utils.testbook.TestBook;
import io.github.giulong.spectrum.utils.tests_comparators.TestsComparator;
import io.github.giulong.spectrum.utils.video.Video;
import lombok.Generated;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.openqa.selenium.chromium.ChromiumDriverLogLevel;
import org.openqa.selenium.firefox.FirefoxDriverLogLevel;

import java.io.File;
import java.net.URL;
import java.time.Duration;
import java.util.*;
import java.util.logging.Level;

import static lombok.AccessLevel.PRIVATE;

@SuppressWarnings("unused")
@Getter
@NoArgsConstructor(access = PRIVATE)
public class Configuration {

    private static final Configuration INSTANCE = new Configuration();

    @JsonPropertyDescription("Common vars to interpolate other String values in the configuration")
    private Map<String, Object> vars;

    @JsonPropertyDescription("Variables related to the runtime environment, meaning the machine where the tests will run, for example your local pc or a remote server")
    private Runtime runtime;

    @JsonPropertyDescription("Application under test")
    private Application application;

    @JsonPropertyDescription("Execution video recording")
    private Video video;

    @JsonPropertyDescription("Execution summary")
    private Summary summary;

    @JsonPropertyDescription("Extent Report configuration")
    private Extent extent;

    @JsonPropertyDescription("Environments configuration")
    private Environments environments;

    @JsonPropertyDescription("Drivers configuration")
    private Drivers drivers;

    @JsonPropertyDescription("Data models")
    private Data data;

    @JsonPropertyDescription("TestBook (coverage)")
    @JsonSerialize(using = ToStringSerializer.class)
    private TestBook testBook;

    @JsonPropertyDescription("FreeMarker template engine configuration. See https://freemarker.apache.org/")
    private FreeMarker freeMarker;

    @JsonPropertyDescription("Datafaker configuration. See https://www.datafaker.net/documentation/getting-started/")
    private Faker faker;

    @JsonPropertyDescription("Events consumers, such as those to send email notifications, for example")
    private List<EventsConsumer> eventsConsumers;

    public static Configuration getInstance() {
        return INSTANCE;
    }

    @Getter
    @Generated
    public static class Runtime {

        @JsonPropertyDescription("Profiles to be activated. By default, it's 'local'")
        private String profiles;

        @JsonSerialize(using = ToStringSerializer.class)
        @JsonSchemaTypes(String.class)
        @JsonPropertyDescription("Active runtime environment")
        private Environment environment;

        @JsonSerialize(using = ToStringSerializer.class)
        @JsonSchemaTypes(String.class)
        @JsonPropertyDescription("Driver to use")
        private Driver<?, ?, ?> driver;

        @JsonPropertyDescription("Folder where you will store files to be checked against downloaded ones")
        private String filesFolder;

        @JsonPropertyDescription("Destination folder for files downloaded during the execution")
        private String downloadsFolder;

        @JsonPropertyDescription("Cache folder for storing Spectrum internal cross-executions data")
        private String cacheFolder;
    }

    @Getter
    @Generated
    public static class Application {

        @JsonPropertyDescription("Application's under test base url")
        private String baseUrl;

        @JsonPropertyDescription("Highlight the web elements the test interacts with. Useful to visually debug the execution")
        private Highlight highlight;

        @Getter
        @Generated
        public static class Highlight {

            @JsonIgnore
            @JacksonInject("enabledFromClient")
            private boolean enabled;

            @JsonPropertyDescription("Path to the js used to highlight. Relative to the resources folder")
            private String js;
        }
    }

    @Getter
    @Generated
    public static class Extent {

        @JsonPropertyDescription("Title of the html page")
        private String documentTitle;

        @JsonPropertyDescription("Where to generate the report")
        private String reportFolder;

        @JsonPropertyDescription("Name shown in the header of the report")
        private String reportName;

        @JsonPropertyDescription("Name of the report file .You can use the ${timestamp} placeholder, which will be resolved at runtime")
        private String fileName;

        @JsonPropertyDescription("Theme used. Can be STANDARD or DARK")
        private String theme;

        @JsonPropertyDescription("Timestamp of each test's start-time and end-time")
        private String timeStampFormat;

        @JsonPropertyDescription("Regex to extract the WebElement's selector, when the driver fires an event")
        private String locatorRegex;

        @JsonPropertyDescription("Retention rules configuration")
        private Retention retention;

        @JsonPropertyDescription("Set to true to generate an inline report alongside the main one")
        private boolean inline;

        @JsonPropertyDescription("Where to generate the inline report")
        private String inlineReportFolder;

        @JsonPropertyDescription("Set to true if you want the report to be automatically opened when the suite execution is finished")
        private boolean openAtEnd;

        @JsonPropertyDescription("Path to the custom css to apply. Relative to the resources folder")
        private String css;

        @JsonPropertyDescription("Path to the custom js to apply. Relative to the resources folder")
        private String js;

        @JsonPropertyDescription("How to sort tests in the produced report")
        private TestsComparator sort;
    }

    @Getter
    @Generated
    public static class Drivers {

        @JsonPropertyDescription("Whether to keep the driver open after the execution")
        private boolean keepOpen;

        @JsonPropertyDescription("Whether to enable the BiDi protocol instead of CDP")
        private boolean biDi;

        @JsonPropertyDescription("Driver's fluent waits")
        private Waits waits;

        @JsonPropertyDescription("Chrome capabilities. See: https://www.selenium.dev/documentation/webdriver/browsers/chrome/")
        private Chrome chrome;

        @JsonPropertyDescription("Firefox capabilities. See: https://www.selenium.dev/documentation/webdriver/browsers/firefox/")
        private Firefox firefox;

        @JsonPropertyDescription("Edge capabilities. See: https://www.selenium.dev/documentation/webdriver/browsers/edge/")
        private Edge edge;

        @JsonPropertyDescription("Safari capabilities. See: https://www.selenium.dev/documentation/webdriver/browsers/safari/")
        private Safari safari;

        @JsonPropertyDescription("Android UiAutomator2 capabilities. See: https://github.com/appium/appium-uiautomator2-driver#capabilities")
        private UiAutomator2 uiAutomator2;

        @JsonPropertyDescription("Android Espresso capabilities. See: https://github.com/appium/appium-espresso-driver#capabilities")
        private Espresso espresso;

        @JsonPropertyDescription("XCUITest capabilities. See: https://github.com/appium/appium-xcuitest-driver")
        private XCUITest xcuiTest;

        @JsonPropertyDescription("Windows capabilities. See: https://github.com/appium/appium-windows-driver")
        private Windows windows;

        @JsonPropertyDescription("Mac2 capabilities. See: https://github.com/appium/appium-mac2-driver")
        private Mac2 mac2;

        @JsonPropertyDescription("Appium generic capabilities. See: https://github.com/appium/java-client#drivers-support")
        private AppiumGeneric appiumGeneric;

        @JsonPropertyDescription("Driver's internal logging levels")
        private Logs logs;

        @JsonPropertyDescription("Events fired by the driver, automatically logged and added to the report according to the log level set when running the suite")
        private Events events;

        @Getter
        @Generated
        public static class Waits {

            @JsonPropertyDescription("Seconds Selenium waits before throwing a NoSuchElementException when an element isn't found")
            @JsonSchemaTypes(int.class)
            private Duration implicit;

            @JsonPropertyDescription("Seconds that Selenium waits before throwing an exception because the page wasn't fully loaded yet")
            @JsonSchemaTypes(int.class)
            private Duration pageLoadTimeout;

            @JsonPropertyDescription("Seconds that Selenium waits before throwing a ScriptTimeoutException")
            @JsonSchemaTypes(int.class)
            private Duration scriptTimeout;

            @JsonPropertyDescription("WebDriverWait injected in test classes/pages that you can use on file download")
            @JsonSchemaTypes(int.class)
            private Duration downloadTimeout;

            @JsonPropertyDescription("Auto-wait configuration")
            private AutoWait auto;

            @Getter
            @Generated
            public static class AutoWait {

                @JsonPropertyDescription("Whether to enable the auto-wait. True by default")
                private boolean enabled;

                @JsonPropertyDescription("Timeout in seconds")
                @JsonSchemaTypes(int.class)
                public Duration timeout;
            }
        }

        public interface BiDiDriverConfiguration {
            boolean isBiDi();
        }

        @Getter
        @Generated
        public static class Chrome implements BiDiDriverConfiguration {

            @JsonPropertyDescription("Whether to enable the BiDi protocol instead of CDP")
            private boolean biDi;

            @JsonPropertyDescription("Chrome's args")
            private List<String> args;

            @JsonPropertyDescription("Chrome's capabilities")
            private Map<String, Object> capabilities;

            @JsonPropertyDescription("Chrome's experimental options")
            private Map<String, Object> experimentalOptions;

            @JsonPropertyDescription("Chrome service options")
            private Service service;

            @Getter
            @Generated
            public static class Service {
                private boolean buildCheckDisabled;
                private boolean appendLog;
                private boolean readableTimestamp;
                private ChromiumDriverLogLevel logLevel;
                private boolean silent;
                private boolean verbose;
                private String allowedListIps;
            }
        }

        @Getter
        @Generated
        public static class Firefox implements BiDiDriverConfiguration {

            @JsonPropertyDescription("Whether to enable the BiDi protocol instead of CDP")
            private boolean biDi;

            @JsonPropertyDescription("Absolute path to the custom Firefox binary to use")
            private String binary;

            @JsonPropertyDescription("Firefox's args")
            private List<String> args;

            @JsonPropertyDescription("Firefox's preferences")
            private Map<String, Object> preferences;

            @JsonPropertyDescription("Firefox's capabilities")
            private Map<String, Object> capabilities;

            @JsonPropertyDescription("Firefox service options")
            private Service service;

            @Getter
            @Generated
            public static class Service {
                private String allowHosts;
                private FirefoxDriverLogLevel logLevel;
                private boolean truncatedLogs;

                @JsonSchemaTypes(String.class)
                private File profileRoot;
            }
        }

        @Getter
        @Generated
        public static class Edge implements BiDiDriverConfiguration {

            @JsonPropertyDescription("Whether to enable the BiDi protocol instead of CDP")
            private boolean biDi;

            @JsonPropertyDescription("Edge's args")
            private List<String> args;

            @JsonPropertyDescription("Edge's capabilities")
            private Map<String, Object> capabilities;

            @JsonPropertyDescription("Edge's experimental options")
            private Map<String, Object> experimentalOptions;

            @JsonPropertyDescription("Edge service options")
            private Chrome.Service service;
        }

        @Getter
        @Generated
        public static class Safari {

            @JsonPropertyDescription("Safari service options")
            private Service service;

            @Getter
            @Generated
            public static class Service {
                private boolean logging;
            }
        }

        @Getter
        @Generated
        public static class UiAutomator2 {

            @JsonPropertyDescription("Android UiAutomator2's capabilities")
            private Map<String, Object> capabilities;
        }

        @Getter
        @Generated
        public static class Espresso {

            @JsonPropertyDescription("Android Espresso's capabilities")
            private Map<String, Object> capabilities;
        }

        @Getter
        @Generated
        public static class XCUITest {

            @JsonPropertyDescription("iOS XCUITest's capabilities")
            private Map<String, Object> capabilities;
        }

        @Getter
        @Generated
        public static class Windows {

            @JsonPropertyDescription("Windows' capabilities")
            private Map<String, Object> capabilities;
        }

        @Getter
        @Generated
        public static class Mac2 {

            @JsonPropertyDescription("Mac2's capabilities")
            private Map<String, Object> capabilities;
        }

        @Getter
        @Generated
        public static class AppiumGeneric {

            @JsonPropertyDescription("Appium generic's capabilities")
            private Map<String, Object> capabilities;
        }

        @Getter
        @Generated
        public static class Logs {

            @JsonPropertyDescription("The level at which driver's logs will be logged in Spectrum (execution) logs")
            @JsonSchemaTypes(value = String.class, valueList = {"ERROR", "WARN", "INFO", "DEBUG", "TRACE"})
            private org.slf4j.event.Level level;

            @JsonSchemaTypes(value = String.class, valueList = {"OFF", "SEVERE", "WARNING", "INFO", "CONFIG", "FINE", "FINER", "FINEST", "ALL"})
            private Level browser;

            @JsonSchemaTypes(value = String.class, valueList = {"OFF", "SEVERE", "WARNING", "INFO", "CONFIG", "FINE", "FINER", "FINEST", "ALL"})
            private Level driver;

            @JsonSchemaTypes(value = String.class, valueList = {"OFF", "SEVERE", "WARNING", "INFO", "CONFIG", "FINE", "FINER", "FINEST", "ALL"})
            private Level performance;
        }

        @Getter
        @Generated
        public static class Events {
            private Event beforeAnyCall;
            private Event afterAnyCall;
            private Event onError;
            private Event beforeAnyWebDriverCall;
            private Event afterAnyWebDriverCall;
            private Event beforeGet;
            private Event afterGet;
            private Event beforeGetCurrentUrl;
            private Event afterGetCurrentUrl;
            private Event beforeGetTitle;
            private Event afterGetTitle;
            private Event beforeFindElement;
            private Event afterFindElement;
            private Event beforeFindElements;
            private Event afterFindElements;
            private Event beforeGetPageSource;
            private Event afterGetPageSource;
            private Event beforeClose;
            private Event afterClose;
            private Event beforeQuit;
            private Event afterQuit;
            private Event beforeGetWindowHandles;
            private Event afterGetWindowHandles;
            private Event beforeGetWindowHandle;
            private Event afterGetWindowHandle;
            private Event beforeExecuteScript;
            private Event afterExecuteScript;
            private Event beforeExecuteAsyncScript;
            private Event afterExecuteAsyncScript;
            private Event beforePerform;
            private Event afterPerform;
            private Event beforeResetInputState;
            private Event afterResetInputState;
            private Event beforeAnyWebElementCall;
            private Event afterAnyWebElementCall;
            private Event beforeClick;
            private Event afterClick;
            private Event beforeSubmit;
            private Event afterSubmit;
            private Event beforeSendKeys;
            private Event afterSendKeys;
            private Event beforeClear;
            private Event afterClear;
            private Event beforeGetTagName;
            private Event afterGetTagName;
            private Event beforeGetAttribute;
            private Event afterGetAttribute;
            private Event beforeIsSelected;
            private Event afterIsSelected;
            private Event beforeIsEnabled;
            private Event afterIsEnabled;
            private Event beforeGetText;
            private Event afterGetText;
            private Event beforeFindWebElement;
            private Event afterFindWebElement;
            private Event beforeFindWebElements;
            private Event afterFindWebElements;
            private Event beforeIsDisplayed;
            private Event afterIsDisplayed;
            private Event beforeGetLocation;
            private Event afterGetLocation;
            private Event beforeGetSize;
            private Event afterGetSize;
            private Event beforeGetCssValue;
            private Event afterGetCssValue;
            private Event beforeAnyNavigationCall;
            private Event afterAnyNavigationCall;
            private Event beforeTo;
            private Event afterTo;
            private Event beforeBack;
            private Event afterBack;
            private Event beforeForward;
            private Event afterForward;
            private Event beforeRefresh;
            private Event afterRefresh;
            private Event beforeAnyAlertCall;
            private Event afterAnyAlertCall;
            private Event beforeAccept;
            private Event afterAccept;
            private Event beforeDismiss;
            private Event afterDismiss;
            private Event beforeAnyOptionsCall;
            private Event afterAnyOptionsCall;
            private Event beforeAddCookie;
            private Event afterAddCookie;
            private Event beforeDeleteCookieNamed;
            private Event afterDeleteCookieNamed;
            private Event beforeDeleteCookie;
            private Event afterDeleteCookie;
            private Event beforeDeleteAllCookies;
            private Event afterDeleteAllCookies;
            private Event beforeGetCookies;
            private Event afterGetCookies;
            private Event beforeGetCookieNamed;
            private Event afterGetCookieNamed;
            private Event beforeAnyTimeoutsCall;
            private Event afterAnyTimeoutsCall;
            private Event beforeImplicitlyWait;
            private Event afterImplicitlyWait;
            private Event beforeSetScriptTimeout;
            private Event afterSetScriptTimeout;
            private Event beforePageLoadTimeout;
            private Event afterPageLoadTimeout;
            private Event beforeAnyWindowCall;
            private Event afterAnyWindowCall;
            private Event beforeGetWindowSize;
            private Event afterGetWindowSize;
            private Event beforeSetSize;
            private Event afterSetSize;
            private Event beforeGetPosition;
            private Event afterGetPosition;
            private Event beforeSetPosition;
            private Event afterSetPosition;
            private Event beforeMaximize;
            private Event afterMaximize;
            private Event beforeFullscreen;
            private Event afterFullscreen;
        }

        @Getter
        @Generated
        public static class Event {

            @JsonPropertyDescription("Level at which this event will be logged")
            @JsonSchemaTypes(value = String.class, valueList = {"ERROR", "WARN", "INFO", "DEBUG", "TRACE"})
            private org.slf4j.event.Level level;

            @JsonPropertyDescription("Message to be logged upon receiving this event")
            private String message;

            @JsonPropertyDescription("Milliseconds to wait before listening to this event")
            private long wait;
        }
    }

    @Getter
    @Generated
    public static class Environments {

        @JsonPropertyDescription("Local environment configuration")
        private Local local;

        @JsonPropertyDescription("Grid environment configuration")
        private Grid grid;

        @JsonPropertyDescription("Appium environment configuration")
        private Appium appium;

        @Getter
        @Generated
        public static class Local {
        }

        @Getter
        @Generated
        public static class Grid {

            @JsonSchemaTypes(String.class)
            @JsonPropertyDescription("Url of the selenium grid")
            private URL url;

            @JsonPropertyDescription("Capabilities dedicated to executions on the grid")
            private final Map<String, Object> capabilities = new HashMap<>();

            @JsonPropertyDescription("Whether to search for files to upload on the client machine or not")
            private boolean localFileDetector;
        }

        @Getter
        @Generated
        public static class Appium extends Grid {

            @JsonPropertyDescription("Set to true to redirect server logs to Spectrum's logs, at the level specified in the drivers.logs.level node")
            private boolean collectServerLogs;

            @JsonPropertyDescription("Appium service options")
            private Service service;

            @Getter
            @Generated
            public static class Service {

                @JsonPropertyDescription("IP address of the Appium server")
                private String ipAddress;

                @JsonPropertyDescription("Sets which port the appium server should be started on. A value of 0 indicates that any free port may be used")
                private int port;

                @JsonPropertyDescription("Sets timeout in seconds")
                @JsonSchemaTypes(int.class)
                private Duration timeout;
            }
        }
    }

    @Getter
    @Generated
    public static class Data {

        @JsonPropertyDescription("sub-folder under src/test/resources where to find your data*.yaml")
        private String folder;
    }

    @Getter
    @Generated
    public static class FreeMarker {

        @JsonPropertyDescription("FreeMarker version. See https://freemarker.apache.org/docs/app_versions.html")
        private String version;

        @JsonSchemaTypes(String.class)
        @JsonPropertyDescription("Locale to be used. See https://freemarker.apache.org/docs/ref_directive_setting.html")
        private Locale locale;

        @JsonPropertyDescription("Number format to be used. See https://freemarker.apache.org/docs/ref_directive_setting.html")
        private String numberFormat;
    }

    @Getter
    @Generated
    public static class Faker {

        @JsonSchemaTypes(String.class)
        @JsonPropertyDescription("Locale to be used. See https://www.datafaker.net/documentation/getting-started/")
        private Locale locale;

        @JsonSchemaTypes(int.class)
        @JsonPropertyDescription("Random seed to be used. See https://www.datafaker.net/documentation/getting-started/")
        private Random random;
    }
}