The modal steps cover HTML modal overlays end to end: asserting a modal is (or is not) on screen, checking its title and body text, clicking buttons inside it by text or by CSS selector, and closing it again. Modal flows are notoriously flaky in browser tests - overlays animate in, close buttons vary wildly between frameworks, and a "visible" check based on offsetParent lies for fixed-position containers. These steps are built around those realities.
How a modal is found is driven by the selector registry: if a modal selector is registered - either directly or via an active CMS / framework preset (Bootstrap, MUI, Drupal admin, and so on all ship one) - that selector wins; otherwise the steps fall back to the standards-based [role="dialog"], dialog. Visibility is derived from computed style (display, visibility, opacity) rather than offsetParent, which makes the check reliable for fixed-position modal containers.
The phrasings are deliberately flexible: I/we, a/the, and a trailing dialog are all optional, so Then I should see a modal and Then we should not see the modal dialog hit the same step. The interaction steps wait up to 10 seconds for a visible modal before acting, and the companion steps When I wait for the modal to appear / to disappear live in the wait steps group - use them around animated open/close transitions.
Quick reference
| Step | What it does |
|---|---|
Then I should see a modal | Assert a modal dialog is visible (add not for the inverse). |
Then I should see a modal with title "Confirm Action" | Assert a modal with the given title (heading text or title/aria-label) is visible. |
Then I should see a "confirmation-modal" modal | Assert a specific modal by id, class, or data-modal value is visible. |
Then I should see "Are you sure?" in the modal | Assert the modal contains (or with not, lacks) the given text. |
Then the modal should contain "Welcome modal text" | Alternate phrasing - checks the modal's plain innerText. |
Then the modal should not contain "Goodbye" | Assert the modal lacks the text (passes if no modal exists). |
When I click "Confirm" in the modal | Click a button or link inside the modal by its visible text. |
When I click on "#close-btn" in the modal | Click a CSS-selector-addressed element inside the modal. |
When I close the modal | Close via a recognized close button, or Escape as fallback (also: dismiss). |
Steps in detail
Then I should see a modal / Then I should not see a modal
Asserts whether any modal (per the registered or fallback selector) is currently visible, using the computed-style visibility check. This is a point-in-time assertion - if the modal animates open, precede it with When I wait for the modal to appear.
When I click "show-modal" by attr
And I wait for the modal to appear
Then I should see a modal
Then I should see a modal with title "Confirm Action"
Asserts a modal with the given title is visible (or not, with not). The title is matched two ways: as text inside a heading element within the modal (.modal-title, .dialog-title, h1-h3), or as a substring of a title / aria-label attribute. Either match counts, so it works for both classic Bootstrap-style titles and ARIA-labelled dialogs.
Then I should see a modal with title "Welcome"
And I should not see a modal with title "Error"
Then I should see a "confirmation-modal" modal
Targets one specific modal when several could exist. If the identifier starts with # or . it is used as a raw CSS selector; a bare name is tried as an id, a class, and a data-modal attribute value. Visibility is checked on the first match.
When I click "delete-btn" by attr
And I wait for modal to appear
Then I should see a "delete-confirmation-modal" modal
Then I should see "Are you sure?" in the modal
Asserts that the visible modal contains (or, with not, does not contain) the given text anywhere inside it. Uses a locator text filter, so nested markup does not matter.
Then I should see "Are you sure you want to delete this item?" in the modal
And I should not see "Error occurred" in the modal
Then the modal should contain "Welcome modal text" / should not contain
Alternate phrasing that reads the modal's plain innerText and does a substring check - use it when the text is part of the modal body rather than a specific element; on failure it prints the actual text found. The negative form is lenient by design: it passes when no modal exists at all or when a modal exists without the text, which makes it safe to use right after closing a modal.
When I close the modal
Then the modal should not contain "Welcome"
When I click "Confirm" in the modal
Waits up to 10 seconds for a visible modal, then clicks the first clickable element inside it - button, a, [role="button"], submit/button inputs, or .btn - whose text matches. Variants like click "OK" button in the modal dialog are accepted. On failure you get a friendly error suggesting you open the modal first and check the button text.
When I click "trigger-modal" by attr
And I wait for the modal to appear
When I click "Confirm" in the modal
Then I should not see a modal
When I click on "#close-btn" in the modal
Clicks a CSS-selector-addressed element scoped to the active modal. Distinct from the text variant above - use it for icon buttons with no readable text, or when the same text appears on multiple buttons and you need the selector to disambiguate.
When I click on "button[aria-label=Close]" in the modal
And I click on "[data-testid=accept]" in the modal
When I close the modal / When I dismiss the modal
Waits for a visible modal, then looks for a recognized close control - .close, .modal-close, [data-dismiss="modal"], [aria-label="Close"], .btn-close, or any button with "close" in its class - and clicks it. If no close button is visible it presses Escape on the modal instead. If neither works, the error suggests registering a custom close-button selector.
When I close the modal
And I wait for the modal to disappear
Then I should not see the modal
Complete example
Adapted from tests/features/modal-basic-example.feature, running against the examples/test--modal-dialogs.html fixture:
Feature: Modal Dialog - Basic Interactions
Background:
Given I am on "/test--modal-dialogs.html"
Scenario: Open, verify, and confirm a modal
Then I should not see a modal
When I click "show-modal" by attr
And I wait for the modal to appear
Then I should see a modal
And I should see "This is a sample modal dialog" in the modal
When I click "Confirm" in the modal
And I wait 1 second
Then I should not see a modal
Scenario: Verify specific modal by identifier
When I click "delete-btn" by attr
And I wait for modal to appear
Then I should see a "delete-confirmation-modal" modal
And I should see "Are you sure you want to delete this item?" in the modal
When I click "Cancel" button in the modal dialog
And I wait 1 second
Then I should not see the modal dialog
Tips
- If your framework's modals are not detected, register a
modalselector (and relatedmodal close,modal titlekeys) in the selector registry, or activate the matching CMS/framework preset - every preset exposes the canonical modal keys. - These steps target in-page HTML overlays. Native browser dialogs (
alert,confirm,prompt) are handled by the dialog steps instead. - Always bracket animated modals with
When I wait for the modal to appear/to disappearfrom the wait steps group to avoid racing the transition.