DialogElement.java

package org.vaadin.addons.dramafinder.element;

import com.microsoft.playwright.Locator;
import com.microsoft.playwright.Page;
import com.microsoft.playwright.options.AriaRole;
import org.vaadin.addons.dramafinder.element.shared.HasStyleElement;
import org.vaadin.addons.dramafinder.element.shared.HasThemeElement;

import static com.microsoft.playwright.assertions.PlaywrightAssertions.assertThat;

/**
 * PlaywrightElement for {@code <vaadin-dialog>}.
 * <p>
 * Provides access to header/content/footer slots, modal flags and open state.
 */
public class DialogElement extends VaadinElement implements HasThemeElement, HasStyleElement {

    public static final String FIELD_TAG_NAME = "vaadin-dialog";

    /**
     * Create a {@code DialogElement} by resolving the dialog with ARIA role.
     */
    public DialogElement(Page page) {
        super(
                page.getByRole(AriaRole.DIALOG)
                        .and(page.locator(FIELD_TAG_NAME)));
    }

    /** Create a {@code DialogElement} from an existing locator. */
    public DialogElement(Locator locator) {
        super(locator);
    }

    /** Close the dialog using the Escape key. */
    public void closeWithEscape() {
        getLocator().press("Escape");
    }

    /**
     * Locator for the overlay rendered in the dialog's shadow DOM.
     * <p>
     * The {@code <vaadin-dialog>} host element itself has a zero-size bounding
     * box, so Playwright never considers it visible. The actual visible content
     * lives in the {@code <vaadin-dialog-overlay>} inside its shadow root, which
     * Playwright's CSS engine reaches by piercing the open shadow root.
     */
    public Locator getOverlayLocator() {
        return getLocator().locator("vaadin-dialog-overlay");
    }

    /** Whether the dialog is open (visible). */
    public boolean isOpen() {
        return getOverlayLocator().isVisible();
    }

    /** Assert that the dialog is open. */
    public void assertOpen() {
        assertThat(getLocator()).hasAttribute("opened", "");
    }

    /** Whether the dialog (its overlay) is visible. */
    @Override
    public boolean isVisible() {
        return getOverlayLocator().isVisible();
    }

    /** Assert that the dialog overlay is visible. */
    @Override
    public void assertVisible() {
        assertThat(getOverlayLocator()).isVisible();
    }

    /** Assert that the dialog overlay is hidden. */
    @Override
    public void assertHidden() {
        assertThat(getOverlayLocator()).isHidden();
    }

    /** Whether the dialog is modal (i.e. not modeless). */
    public boolean isModal() {
        return getLocator().getAttribute("modeless") == null;
    }

    /** Assert that the dialog is modal. */
    public void assertModal() {
        assertThat(getLocator()).not().hasAttribute("modeless", "");
    }

    /** Assert that the dialog is modeless. */
    public void assertModeless() {
        assertThat(getLocator()).hasAttribute("modeless", "");
    }

    /** Assert that the dialog is closed (its overlay is hidden). */
    public void assertClosed() {
        assertThat(getOverlayLocator()).isHidden();
    }

    /** Get the header text from the title slot. */
    public String getHeaderText() {
        return getLocator().locator("> [slot='title']").textContent();
    }

    /** Assert the header text matches. */
    public void assertHeaderText(String headerText) {
        assertThat(getLocator().locator("> [slot='title']")).hasText(headerText);
    }

    /** Locator for the header content slot. */
    public Locator getHeaderLocator() {
        return getLocator().locator("> [slot='header-content']");
    }

    /** Locator for the dialog content (first non-slotted child). */
    public Locator getContentLocator() {
        // using xpath to not pierce the shadow dom
        return getLocator().locator("xpath=./*[not(@slot)][1]");
    }

    /** Locator for the footer slot. */
    public Locator getFooterLocator() {
        return getLocator().locator("> [slot='footer']");
    }

    /** Get a dialog by its header text (accessible name). */
    public static DialogElement getByHeaderText(Page page, String summary) {
        return new DialogElement(
                page.getByRole(AriaRole.DIALOG, new Page.GetByRoleOptions().setName(summary))
                        .and(page.locator(FIELD_TAG_NAME))
        );
    }

}