package at.porscheinformatik.seleniumcomponents;

import java.util.Objects;

import org.openqa.selenium.NoSuchElementException;
import org.openqa.selenium.WebElement;

/**
 * The base class for components accessible by the Selenium tests. A {@link SeleniumComponent} usually describes a node
 * in the DOM.
 *
 * @author ham
 */
public interface SeleniumComponent extends WebElementContainer
{

    SeleniumLogger LOG = new SeleniumLogger(SeleniumComponent.class);

    /**
     * Returns the parent of this component.
     *
     * @return the parent, may be null if the component is root (like a page).
     */
    SeleniumComponent parent();

    /**
     * Returns the {@link SeleniumEnvironment} of this component, which is, by default, the environment of the parent.
     * If this component is root, it must implement this method and return a valid environment.
     *
     * @return the environment, never null
     */
    default SeleniumEnvironment environment()
    {
        SeleniumComponent parent = parent();

        Objects
            .requireNonNull(parent, () -> String
                .format(
                    "The parent is null which means, that this component is root. Implement this method by supplying a valid environment"));

        return Objects
            .requireNonNull(parent.environment(),
                () -> String.format("The environment of the component defined by %s is null", parent.getClass()));
    }

    /**
     * Returns true if a {@link WebElement} described by this component is ready and available (it must not be visible,
     * though). This method has no timeout, it does not wait for the component to become existent.
     *
     * @return true if the component exists
     */
    boolean isReady();

    /**
     * Waits until the component becomes ready.
     *
     * @param timeoutInSeconds the timeout in seconds
     */
    default void waitUntilReady(double timeoutInSeconds)
    {
        SeleniumAsserts
            .assertThatSoon(timeoutInSeconds, "Component becomes ready: " + describe(), () -> this,
                SeleniumMatchers.isReady());
    }

    /**
     * Returns true if a {@link WebElement} described by this component is visible. By default it checks, if the element
     * is visible. This method has no timeout, it does not wait for the component to become existent. <br>
     * <br>
     * It it NO good idea, to check for invisibility. If the component is not visible, Selenium always waits for some
     * time. This causes tests to run slowly and timeouts to fail.
     *
     * @return true if the component is visible
     */
    default boolean isVisible()
    {
        try
        {
            return SeleniumUtils.retryOnStale(() -> element().isDisplayed());
        }
        catch (NoSuchElementException e)
        {
            return false;
        }
    }

    /**
     * Waits until the component becomes ready.
     *
     * @param timeoutInSeconds the timeout in seconds
     */
    default void waitUntilVisible(double timeoutInSeconds)
    {
        SeleniumAsserts
            .assertThatSoon(timeoutInSeconds, "Component becomes visible: " + describe(), () -> this,
                SeleniumMatchers.isVisible());
    }

    @Override
    String toString();
}

@@ -66,24 +66,71 @@
 l.Objects;%0A%0A
+import org.openqa.selenium.JavascriptExecutor;%0A
 import org.o
@@ -3270,24 +3270,1359 @@
 ());%0A    %7D%0A%0A
+    /**%0A     * This method is not intended to be used in regular test cases. Its main use is to highlight a element while%0A     * debugging a test.%0A     * %0A     * %3Cp%3E%0A     * Sometime there are multiple elements on the page that match a given selector. Then it is hard to find, why a test%0A     * breaks. It might use the wrong element. Using the highlight method should make it farily easy to see if the%0A     * correct element is selected.%0A     * %3C/p%3E%0A     * %0A     * %3Cp%3E%0A     * This method will set a border and background for the element. In most of the cases this should be enough to see%0A     * if the correct element is selected. But sometimes elements are not directly visible on screen, because they have%0A     * no size. For this reason, a %22hightlighted=true%22 attribute is added to the element. You can then execute the%0A     * following script in the DevTools of your brower to find all highlighted elements.%0A     * %3Ccode%3Edocument.querySelectorAll('%5Bhighlighted%5D')%3C/code%3E%0A     * %3C/p%3E%0A     */%0A    default void highlight()%0A    %7B%0A        JavascriptExecutor executor = (JavascriptExecutor) environment().getDriver();%0A%0A        executor%0A            .executeScript(%22arguments%5B0%5D.setAttribute('style', 'background: yellow; border: 2px solid red;'); %22%0A                + %22arguments%5B0%5D.setAttribute('highlighted', 'true')%22, element());%0A    %7D%0A%0A
     @Overrid

Add highlight method to component