Skip to main content

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'
Key Points
  • 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

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'
Integration Pattern

Use Karate to:

  1. Set up test data via API calls
  2. Run UI automation to test user workflows
  3. 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

CommandDescriptionExample
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

CheckDescriptionExample
.existsElement is present* waitFor('#btn').exists
.hiddenElement is not visible* waitFor('#loading').hidden
.enabledElement is interactive* waitFor('#submit').enabled
.attribute('name')Get attribute value* match attribute('#link', 'href') contains '/home'

Next Steps