Skip to main content

UI Testing Guide

Comprehensive guide to browser-based UI testing with @esimplicity/stack-tests.

Overview

UI testing validates browser-based interfaces using Playwright. Use @ui tagged steps for user interaction testing.

Basic Navigation

@ui
Scenario: Navigate to page
Given I navigate to "/login"
Then the URL should contain "/login"

Full URL

@ui
Scenario: External navigation
Given I open "https://example.com" in the browser

Browser Actions

@ui
Scenario: Browser navigation
Given I navigate to "/page1"
When I click the link "Next"
Then the URL should contain "/page2"
When I go back in the browser
Then the URL should contain "/page1"
When I reload the page

Interacting with Elements

Clicking

@ui
Scenario: Click interactions
# Click button by name
When I click the button "Submit"

# Click link by text
When I click the link "Learn More"

Advanced Clicking

@ui
Scenario: Click by locator
# Click element containing text
When I "click" the "div" element that contains "Click Me"

# Click by test ID
When I "click" the "1st" element with "submit-btn" "test ID"

# Click by label
When I "click" the "1st" element with "Username" "label"

# Force click (bypasses visibility checks)
When I "force click" the "1st" element with "hidden-btn" "test ID"

Click Modes

ModeDescription
clickStandard click
force clickBypasses visibility checks
dispatch clickDispatches click event
force dispatch clickForce + dispatch

Form Interactions

Filling Fields

@ui
Scenario: Fill form fields
# By label
When I fill the field "Email" with "user@example.com"
When I fill the field "Password" with "secret123"

# By placeholder
When I fill the placeholder "Enter your name" with "John Doe"

Input Modes

@ui
Scenario: Different input methods
# Type (character by character)
When I "type" "hello" in the "1st" element with "search" "placeholder"

# Fill (instant)
When I "fill" "hello" in the "1st" element with "search" "placeholder"

# Choose (dropdown)
When I "choose" "Option A" in the "1st" element with "Select..." "placeholder"
@ui
Scenario: Select from dropdown
Then I fill "United States" into the "Country" dropdown

# Or using select step
When I select from dropdown "Country" value "United States"

Keyboard Input

@ui
Scenario: Keyboard actions
# Type text
Then I "type" "search query"

# Press keys
Then I "press" "Enter"
Then I "press" "Tab"
Then I "press" "Escape"

Assertions

Text Visibility

@ui
Scenario: Text assertions
Then I should see text "Welcome"
Then I should see text "Hello, {username}"

URL Assertions

@ui
Scenario: URL assertions
Then the URL should contain "/dashboard"
Then I verify if the URL "contains" "/dashboard"
Then I verify if the URL "equals" "http://localhost:3000/dashboard"
Then I verify if the URL "doesntContain" "/login"

Element Visibility

@ui
Scenario: Element visibility
# Check element with text is visible
Then I verify that a "button" element with "Submit" text "is" visible
Then I verify that a "div" element with "Error" text "is not" visible

Element State

@ui
Scenario: Element states
# Immediate check
Then I verify that "1st" element with "Submit" "text" is "visible"
Then I verify that "1st" element with "Submit" "text" is "enabled"
Then I verify that "1st" element with "Email" "label" is "editable"

# With timeout
Then I verify that "1st" element with "Submit" "text" becomes "visible" during "5" seconds

Element States

StateDescription
visibleElement is visible
hiddenElement is hidden
enabledElement is enabled
disabledElement is disabled
editableElement is editable
read-onlyElement is read-only

New Tab Assertions

@ui
Scenario: New tab opened
When I click the link "Open in new tab"
Then I verify if a new tab which URL "contains" "/new-page" opens

Locator Methods

MethodDescriptionExample
textBy visible text"Submit" "text"
labelBy label text"Email" "label"
placeholderBy placeholder"Enter name" "placeholder"
roleBy ARIA role"button" "role"
test IDBy data-testid"submit-btn" "test ID"
alternative textBy alt text"Logo" "alternative text"
titleBy title attribute"Help" "title"
locatorCSS/XPath selector"#submit" "locator"

Waiting

Explicit Waits

@ui
Scenario: Wait for conditions
# Wait fixed time
Then I wait "2" seconds

# Wait for page load
Then I wait for the page to load

Implicit Waits

Most assertions automatically wait:

@ui
Scenario: Auto-waiting
# Waits up to timeout for text to appear
Then I should see text "Loading complete"

URL and State Capture

Save URL

@ui
Scenario: Capture URL
When I save the current URL as "startPage"
# Navigate around...
Given I navigate to "{startPage}"

Extract URL Parts

@ui
Scenario: Extract from URL
# URL: /users/123/profile
When I get a part of the URL based on "/users/(\d+)/" regular expression and save it as "userId"
# userId = "123"

Visual Testing

Zoom

@ui
Scenario: Test at different zoom levels
Then I zoom to "1.5" in the browser
Then I should see text "Content visible at 150%"
Then I zoom to "1" in the browser

Authentication

UI Login

@ui
Scenario: Login via UI
When I log in as admin in UI
Then the URL should contain "/dashboard"

@ui
Scenario: Login via UI as user
When I log in as user in UI
Then I should see text "Welcome"

Complete Examples

Login Flow

@ui
Feature: Login

Scenario: Successful login
Given I navigate to "/login"
When I fill the field "Email" with "user@example.com"
And I fill the field "Password" with "password123"
And I click the button "Sign In"
Then I should see text "Welcome"
And the URL should contain "/dashboard"

Scenario: Failed login
Given I navigate to "/login"
When I fill the field "Email" with "wrong@example.com"
And I fill the field "Password" with "wrongpassword"
And I click the button "Sign In"
Then I should see text "Invalid credentials"
And the URL should contain "/login"

Form Submission

@ui
Feature: Contact Form

Scenario: Submit contact form
Given I navigate to "/contact"
When I fill the field "Name" with "John Doe"
And I fill the field "Email" with "john@example.com"
And I fill the field "Message" with "Hello, this is a test message."
And I fill "General Inquiry" into the "Subject" dropdown
And I click the button "Send Message"
Then I should see text "Thank you for your message"

Multi-Step Wizard

@ui
Feature: Registration Wizard

Scenario: Complete registration
Given I navigate to "/register"

# Step 1: Account
When I fill the field "Email" with "new@example.com"
And I fill the field "Password" with "SecurePass123"
And I click the button "Next"

# Step 2: Profile
Then I should see text "Profile Information"
When I fill the field "First Name" with "John"
And I fill the field "Last Name" with "Doe"
And I click the button "Next"

# Step 3: Confirmation
Then I should see text "Review"
And I should see text "new@example.com"
When I click the button "Complete Registration"
Then I should see text "Registration successful"

Conditional Interactions

@ui
Scenario: Handle optional modal
Given I navigate to "/dashboard"
# Click if visible, ignore if not
When If its visible, I "click" the "1st" element with "Dismiss" "text"
Then I should see text "Dashboard"

Environment Configuration

# .env
FRONTEND_URL=http://localhost:3000
HEADLESS=true

# UI login customization (optional -- these are the defaults)
# UI_LOGIN_PATH=/login
# UI_USERNAME_FIELD=Username
# UI_PASSWORD_FIELD=Password
# UI_LOGIN_BUTTON=Login

Best Practices

Use Semantic Locators

# Good - semantic
When I click the button "Submit"
When I fill the field "Email" with "user@example.com"

# Avoid - implementation-specific
When I "click" the "1st" element with "#btn-submit" "locator"

Independent Scenarios

# Good - self-contained
@ui
Scenario: Edit profile
Given I navigate to "/login"
When I log in as user in UI
And I navigate to "/profile"
And I fill the field "Name" with "Updated Name"
And I click the button "Save"
Then I should see text "Profile updated"

Appropriate Waits

# Good - wait for specific condition
Then I should see text "Data loaded"

# Avoid - arbitrary delays
Then I wait "5" seconds
Then I should see text "Data loaded"

Test IDs for Complex Elements

<!-- In your application -->
<button data-testid="submit-form">Submit</button>
When I "click" the "1st" element with "submit-form" "test ID"

Debugging

Visual Debugging

# Run with headed browser
HEADLESS=false npm test

# Use Playwright UI mode
npx playwright test --ui

# Debug mode
npx playwright test --debug

Trace Viewer

// playwright.config.ts
use: {
trace: 'on-first-retry',
}
npx playwright show-trace trace.zip