EXTENSIONS
UI Testing
Automate cross-browser web testing using Karate's built-in driver for end-to-end scenarios without external Selenium dependencies.
Benefits of UI Testing in Karate
- Unified framework: Combine API and UI testing in the same feature file
- No external dependencies: Built-in Chrome DevTools Protocol support
- Simple syntax: Familiar Gherkin-style commands for browser interactions
- Cross-browser support: Chrome, Firefox, Safari, Edge, and Playwright
Basic Browser Setup
Configure the driver to launch a browser:
Feature: Simple Browser Test
Scenario: Open a webpage
* configure driver = { type: 'chrome' }
* driver 'https://example.com'
* match driver.title == 'Example Domain'
- Use
configure driver
to set browser type driver 'url'
navigates to a webpage- Access page properties like
driver.title
Driver Configuration
Chrome Browser
Scenario: Chrome with options
* configure driver = { type: 'chrome', headless: false }
* driver 'https://example.com'
Headless Mode
Scenario: Run tests without visible browser
* configure driver = { type: 'chrome', headless: true }
* driver 'https://example.com'
Remote WebDriver
// In karate-config.js
var driverConfig = {
type: 'chromedriver',
start: false,
webDriverUrl: 'https://selenium-grid.example.com/wd/hub'
};
karate.configure('driver', driverConfig);
Element Interactions
Click and Input
Scenario: Login form interaction
* configure driver = { type: 'chrome' }
* driver 'https://example.com/login'
* input('#username', 'john@example.com')
* input('#password', 'secret123')
* click('button[type="submit"]')
* match text('#welcome-message') == 'Welcome, John!'
Common Actions
Scenario: Various interactions
* input('#search', 'karate testing')
* click('button.search')
* submit()
* scroll('footer')
* clear('#search')
Waiting Strategies
Wait for Element
Scenario: Wait for dynamic content
* driver 'https://example.com'
* waitFor('#dynamic-content').exists
* match text('#dynamic-content') contains 'Loaded'
Retry Until Condition
Scenario: Retry until data loads
* driver 'https://example.com/dashboard'
* retry until text('#status') == 'Ready'
* click('#start-button')
Element Locator Strategies
CSS Selectors
Use CSS selectors to target elements by ID, class, attributes, or structure:
Scenario: Using CSS selectors
* driver 'https://example.com/login'
# By ID
* click('#login-button')
# By class
* click('.btn-primary')
# By attribute
* click('[data-testid="submit"]')
# Descendant selector
* input('.form-group input[name="email"]', 'user@example.com')
# Pseudo-classes
* click('ul.menu li:first-child')
XPath Expressions
XPath provides powerful element selection based on text content, attributes, or document structure:
Scenario: Using XPath
* driver 'https://example.com/products'
# By text content
* click('//button[text()="Add to Cart"]')
# By partial text
* click('//a[contains(text(), "Learn More")]')
# By attribute
* input('//input[@placeholder="Search products"]', 'laptop')
# Navigate table rows
* def userEmail = text('//tr[td[text()="John"]]/td[2]')
* match userEmail == 'john@example.com'
Friendly Locator Syntax
Karate provides a simplified syntax for common locator patterns:
Scenario: Friendly locators
* driver 'https://example.com'
# Exact text match with tag
* click('{button}Submit')
# Partial text match (starts with)
* click('{a}Learn^')
# Tag with attribute
* input('{input}[name=email]', 'test@example.com')
# Combine with wildcard
* waitFor('{div}Success*').exists
Advanced Element Interactions
Select Dropdowns
Work with dropdown menus using select by text, value, or index:
Scenario: Working with dropdowns
* driver 'https://example.com/form'
# Select by visible text
* select('select[name="country"]', 'United States')
# Select by value attribute
* select('select[name="state"]', { value: 'CA' })
# Select by index position
* select('select[name="city"]', { index: 2 })
# Verify selected option
* match value('select[name="country"]') == 'US'
File Uploads
Upload single or multiple files using file input elements:
Scenario: Upload files
* driver 'https://example.com/upload'
# Upload single file
* input('input[type="file"]', 'classpath:test-data/document.pdf')
# Upload multiple files
* input('#file-input', ['classpath:file1.txt', 'classpath:file2.jpg'])
* click('#upload-button')
* waitFor('.upload-success').exists
Drag and Drop
Perform drag and drop operations using mouse actions:
Scenario: Drag and drop elements
* driver 'https://example.com/kanban'
# Drag from source to target
* mouse('.task-item').down()
* mouse('.completed-column').move()
* mouse().up()
# Alternative syntax
* dragAndDrop('.draggable', '.drop-zone')
Hover and Context Menu
Trigger hover effects and right-click context menus:
Scenario: Mouse hover and right-click
* driver 'https://example.com'
# Hover to reveal menu
* mouse('.dropdown-trigger').move()
* waitFor('.dropdown-menu').exists
* click('.dropdown-menu a[href="/settings"]')
# Right-click context menu
* mouse('#file-item').rightClick()
* click('{div}Delete')
Browser Navigation and Windows
Page Navigation
Navigate browser history and refresh pages:
Scenario: Browser navigation
* driver 'https://example.com/page1'
* click('#next-page')
# Go back
* driver.back()
* match driver.location == 'https://example.com/page1'
# Go forward
* driver.forward()
# Refresh page
* driver.refresh()
# Navigate to new URL
* driver.url = 'https://example.com/page3'
Multiple Windows and Tabs
Switch between browser windows and tabs:
Scenario: Working with multiple windows
* driver 'https://example.com'
# Open link in new tab
* click('#open-new-tab')
# Switch to new window
* switchWindow(1)
* match driver.title contains 'New Page'
# Switch back to original window
* switchWindow(0)
# Close current window
* driver.close()
Working with Iframes
Access elements within iframes:
Scenario: Interact with iframe content
* driver 'https://example.com/editor'
# Switch to iframe by selector
* switchFrame('iframe#editor')
* input('#content', 'Hello from iframe')
# Switch back to main page
* switchFrame(null)
# Switch to iframe by index
* switchFrame(0)
Screenshots and Visual Testing
Capturing Screenshots
Take full-page or element-specific screenshots:
Scenario: Take screenshots
* driver 'https://example.com/dashboard'
# Full page screenshot (saved automatically)
* screenshot()
# Screenshot with custom name
* screenshot('dashboard-view')
# Element screenshot
* screenshot('#analytics-chart')
# Store screenshot as variable
* def chartImage = screenshot('#chart', true)
Screenshot on Failure
Automatically capture screenshots when tests fail:
Scenario: Auto-screenshot on failure
* configure driver = {
type: 'chrome',
screenshotOnFailure: true
}
* driver 'https://example.com'
* click('#submit')
# If this step fails, screenshot is captured automatically
* waitFor('.success-message').exists
Visual Regression Testing
Combine screenshots with image comparison for visual validation:
Scenario: Visual regression test
* driver 'https://example.com/product/123'
* waitFor('#product-image').exists
# Capture product image
* def currentImage = screenshot('#product-image', true)
# Compare with baseline
* compareImage {
baseline: 'classpath:screenshots/product-baseline.png',
latest: currentImage,
failureThreshold: 2
}
Browser Configuration
Chrome Options
Configure Chrome with custom arguments and preferences:
// In karate-config.js
var chromeConfig = {
type: 'chrome',
addOptions: [
'--disable-notifications',
'--disable-popup-blocking',
'--start-maximized',
'--incognito'
],
prefs: {
'download.default_directory': '/tmp/downloads',
'profile.default_content_settings.popups': 0
}
};
karate.configure('driver', chromeConfig);
Browser Window Size
Set browser dimensions for responsive testing:
Scenario: Test at different viewport sizes
* configure driver = {
type: 'chrome',
dimension: { width: 1920, height: 1080 }
}
* driver 'https://example.com'
# Change window size during test
* driver.dimensions = { width: 375, height: 812 }
* waitFor('.mobile-menu').exists
Cross-Browser Configuration
Configure different browsers for cross-browser testing:
// Firefox
var firefoxConfig = {
type: 'firefox',
headless: true,
addOptions: ['-private']
};
// Edge
var edgeConfig = {
type: 'msedge',
addOptions: ['--inprivate']
};
// Safari (macOS only)
var safariConfig = {
type: 'safari'
};
Debugging and Troubleshooting
Interactive Debugging
Pause test execution for manual inspection:
Scenario: Debug with pause
* driver 'https://example.com'
* input('#username', 'admin')
# Pause here to inspect browser state
* karate.stop(5555)
# Test continues after connecting to localhost:5555
* click('#submit')
Console Log Capture
Access browser console logs for debugging:
Scenario: Capture console logs
* driver 'https://example.com/app'
# Trigger action that logs to console
* click('#generate-report')
# Get console logs
* def logs = driver.logs
* print logs
# Assert no errors in console
* match logs !contains 'ERROR'
Highlight Elements
Visually highlight elements during debugging:
Scenario: Highlight elements for debugging
* driver 'https://example.com'
# Highlight element before interaction
* highlight('#important-button')
* delay(1000)
* click('#important-button')
Mobile and Responsive Testing
Mobile Device Emulation
Emulate mobile devices using Chrome DevTools:
// In karate-config.js
var mobileConfig = {
type: 'chrome',
mobileEmulation: {
deviceName: 'iPhone 12 Pro'
}
};
karate.configure('driver', mobileConfig);
Custom Device Metrics
Define custom mobile viewport and user agent:
var customMobileConfig = {
type: 'chrome',
mobileEmulation: {
deviceMetrics: {
width: 390,
height: 844,
pixelRatio: 3
},
userAgent: 'Mozilla/5.0 (iPhone; CPU iPhone OS 15_0 like Mac OS X)'
}
};
Responsive Design Testing
Test responsive layouts at different breakpoints:
Scenario: Test responsive breakpoints
* configure driver = { type: 'chrome' }
* driver 'https://example.com'
# Desktop view (1920x1080)
* driver.dimensions = { width: 1920, height: 1080 }
* waitFor('.desktop-nav').exists
# Tablet view (768x1024)
* driver.dimensions = { width: 768, height: 1024 }
* waitFor('.tablet-menu').exists
# Mobile view (375x667)
* driver.dimensions = { width: 375, height: 667 }
* waitFor('.mobile-hamburger').exists
Mixing API and UI Tests
Feature: E2E Checkout Flow
Scenario: Create order via API, verify in UI
# Setup data via API
Given url apiBaseUrl
And path 'orders'
And request { product: 'Widget', quantity: 2 }
When method post
Then status 201
* def orderId = response.id
# Verify in UI
* configure driver = { type: 'chrome' }
* driver uiBaseUrl + '/orders/' + orderId
* match text('.order-status') == 'Confirmed'
* match text('.quantity') == '2'
Use Karate to:
- Set up test data via API calls
- Run UI automation to test user workflows
- Verify final state with API assertions
This reduces UI test brittleness while ensuring end-to-end coverage.
Remote and Distributed Testing
Selenium Grid
Run tests on remote Selenium Grid for distributed execution:
// In karate-config.js
var gridConfig = {
type: 'chromedriver',
start: false,
webDriverUrl: 'http://selenium-grid:4444/wd/hub'
};
karate.configure('driver', gridConfig);
Docker Containers
Use containerized browsers for consistent test environments:
// Connect to Selenoid or Zalenium
var dockerConfig = {
type: 'chromedriver',
start: false,
webDriverUrl: 'http://localhost:4444/wd/hub',
httpConfig: { readTimeout: 120000 }
};
karate.configure('driver', dockerConfig);
Cloud Services
Integrate with cloud-based browser testing platforms:
// BrowserStack example
var cloudConfig = {
type: 'chromedriver',
start: false,
webDriverUrl: 'https://username:key@hub.browserstack.com/wd/hub',
webDriverSession: {
capabilities: {
browserName: 'chrome',
browserVersion: '120.0',
'bstack:options': {
os: 'Windows',
osVersion: '11'
}
}
}
};
Best Practices
Stable Locator Strategies
Use data attributes for test-specific selectors:
Scenario: Use data-testid attributes
* driver 'https://example.com'
# Preferred: Test-specific attributes
* click('[data-testid="login-button"]')
# Avoid: Generated classes or complex paths
# * click('.MuiButton-root-xyz123')
# * click('div > div > div > button')
Wait for Dynamic Content
Always wait for elements before interacting:
Scenario: Proper wait strategies
* driver 'https://example.com/dashboard'
# Wait for element to exist
* waitFor('#dashboard-content').exists
# Wait for specific text
* waitFor('{div}Dashboard Loaded').exists
# Retry until condition is met
* retry until text('#status') == 'Ready'
# Now safe to interact
* click('#start-button')
Page Object Pattern
Create reusable page components using called features:
# login-page.feature
Scenario: Login action
* input('[data-testid="username"]', username)
* input('[data-testid="password"]', password)
* click('[data-testid="login-button"]')
* waitFor('.dashboard').exists
# main-test.feature
Scenario: Complete user flow
* driver 'https://example.com'
* def loginResult = call read('login-page.feature') {
username: 'admin',
password: 'secret'
}
* match driver.location contains '/dashboard'
Driver Command Reference
Common Driver Commands
Command | Description | Example |
---|---|---|
driver 'url' | Navigate to URL | * driver 'https://example.com' |
click('locator') | Click element | * click('#submit-btn') |
input('locator', value) | Type into element | * input('#email', 'test@example.com') |
select('locator', value) | Select dropdown option | * select('#country', 'USA') |
text('locator') | Get element text | * def txt = text('#message') |
value('locator') | Get input value | * def val = value('#username') |
waitFor('locator') | Wait for element | * waitFor('#loading').hidden |
screenshot() | Take screenshot | * screenshot('page-loaded') |
scroll('locator') | Scroll to element | * scroll('#footer') |
clear('locator') | Clear input field | * clear('#search') |
submit() | Submit form | * submit() |
refresh() | Reload page | * driver.refresh() |
back() | Navigate back | * driver.back() |
forward() | Navigate forward | * driver.forward() |
Element State Checks
Check | Description | Example |
---|---|---|
.exists | Element is present | * waitFor('#btn').exists |
.hidden | Element is not visible | * waitFor('#loading').hidden |
.enabled | Element is interactive | * waitFor('#submit').enabled |
.attribute('name') | Get attribute value | * match attribute('#link', 'href') contains '/home' |
Next Steps
- Learn about visual testing: Image Comparison
- Integrate performance testing: Performance Testing
- Create reusable UI components: Calling Features
- Set up test doubles for backend services: Test Doubles
- View complete examples: Examples and Demos