Skip to content

Latest commit

 

History

History
 
 

karate-core

Folders and files

NameName
Last commit message
Last commit date

parent directory

..
 
 
 
 
 
 

Karate Driver

UI Test Automation Made Simple.

Introduction

This is new, and this first version 0.9.X should be considered experimental.

Especially after the Gherkin parser and execution engine were re-written from the ground-up, Karate is arguably a mature framework that elegantly solves quite a few test-automation engineering challenges - with capabilities such as parallel execution, data-driven testing, environment-switching, powerful assertions, and an innovative UI for debugging.

Which led us to think, what if we could add UI automation without disturbing the core HTTP API testing capabilities. So we gave it a go, and we are releasing the results so far as this experimental version.

Please do note: this is work in progress and all actions needed for test-automation may not be in-place. But we hope that releasing this sooner would result in more users trying this in a variety of environments. And that they provide valuable feedback and even contribute code where possible.

We know too well that UI automation is hard to get right and suffers from 2 big challenges, what we like to call the "flaky test" problem and the "wait for UI element" problem.

With the help of the community, we would like to try valiantly - to see if we can get close to as ideal a state a possible. So wish us luck !

Capabilities

Chrome Java API

Karate also has a Java API to automate the Chrome browser directly, designed for common needs such as converting HTML to PDF or taking a screenshot of a page. You only need the karate-core Maven artifact. Here is an example:

import com.intuit.karate.FileUtils;
import com.intuit.karate.driver.chrome.Chrome;
import java.io.File;
import java.util.Collections;

public class Test {

    public static void main(String[] args) {
        Chrome chrome = Chrome.startHeadless();
        chrome.setLocation("https://github.com/login");
        byte[] bytes = chrome.pdf(Collections.EMPTY_MAP);
        FileUtils.writeToFile(new File("target/github.pdf"), bytes);
        bytes = chrome.screenshot();
        FileUtils.writeToFile(new File("target/github.png"), bytes);
        chrome.quit();
    }
    
}

The parameters that you can optionally customize via the Map argument to the pdf() method are documented here: Page.printToPDF .

If Chrome is not installed in the default location, you can pass a String argument like this: Chrome.startHeadless(executable) or Chrome.start(executable). For more control or custom options, the start() method takes a Map<String, Object> argument where the following keys (all optional) are supported:

  • executable - (String) path to the Chrome executable or batch file that starts Chrome
  • headless - (Boolean) if headless
  • maxPayloadSize - (Integer) defaults to 4194304 (bytes, around 4 MB), but you can override it if you deal with very large output / binary payloads

Syntax Guide

Examples

Web Browser

Windows

Driver Configuration

configure driver

This below declares that the native (direct) Chrome integration should be used, on both Mac OS and Windows - from the default installed location.

* configure driver = { type: 'chrome' }

If you want to customize the start-up, you can use a batch-file:

* configure driver = { type: 'chrome', executable: 'chrome' }

Here a batch-file called chrome can be placed in the system PATH (and made executable) with the following contents:

"/Applications/Google Chrome.app/Contents/MacOS/Google Chrome" $*

For Windows it would be chrome.bat in the system PATH as follows:

"C:\Program Files (x86)\Google\Chrome\Application\chrome" %*

Another example for WebDriver, again assuming that chromedriver is in the PATH:

{ type: 'chromedriver', port: 9515, executable: 'chromedriver' }
key description
type see driver types
executable if present, Karate will attempt to invoke this, if not in the system PATH, you can use a full-path instead of just the name of the executable. batch files should also work
start default true, Karate will attempt to start the executable - and if the executable is not defined, Karate will even try to assume the default for the OS in use
port optional, and Karate would choose the "traditional" port for the given type
headless only applies to type: 'chrome' for now
showDriverLog default false, will include webdriver HTTP traffic in Karate report, useful for troubleshooting or bug reports
showProcessLog default false, will include even executable (webdriver or browser) logs in the Karate report

Driver Types

type default
port
default
executable
description
chrome 9222 mac: /Applications/Google Chrome.app/Contents/MacOS/Google Chrome
win: C:/Program Files (x86)/Google/Chrome/Application/chrome.exe
"native" Chrome automation via the DevTools protocol
chromedriver 9515 chromedriver W3C Chrome Driver
geckodriver 4444 geckodriver W3C Gecko Driver (Firefox)
safaridriver 5555 safaridriver W3C Safari Driver
mswebdriver 17556 MicrosoftWebDriver W3C Microsoft Edge WebDriver
msedge 9222 MicrosoftEdge very experimental - using the DevTools protocol
winappdriver 4727 C:/Program Files (x86)/Windows Application Driver/WinAppDriver Windows Desktop automation, similar to Appium
android 4723 appium android automation via Appium
ios 4723 appium iOS automation via Appium

Locators

The standard locator syntax is supported. For example for web-automation, a / prefix means XPath and else it would be evaluated as a "CSS selector".

And driver.input('input[name=someName]', 'test input')
When driver.submit("//input[@name='commit']")
platform prefix means example
web (none) css selector input[name=someName]
web
android
ios
/ xpath //input[@name='commit']
web ^ link text ^Click Me
web * partial link text *Click Me
win
android
ios
(none) name Submit
win
android
ios
@ accessibility id @CalculatorResults
win
android
ios
# id #MyButton
ios : -ios predicate string :name == 'OK' type == XCUIElementTypeButton
ios ^ -ios class chain ^**/XCUIElementTypeTable[`name == 'dataTable'`]
android - -android uiautomator -input[name=someName]

Keywords

Only one keyword sets up UI automation in Karate, typically by specifying the URL to open in a browser. And then you would use the built-in driver JS object for all other operations, combined with Karate's match syntax for assertions where needed.

driver

Navigate to a web-address and initializes the driver instance for future step operations as per what is configured. And yes, you can use variable expressions from karate-config.js. For example:

Given driver webUrlBase + '/page-01'

driver JSON

A variation where the argument is JSON instead of a URL / address-string, used only if you are testing a desktop (or mobile) application, and for Windows, you can provide the app, appArguments and other parameters expected by the WinAppDriver. For example:

Given driver { app: 'Microsoft.WindowsCalculator_8wekyb3d8bbwe!App' }

JS API

The built-in driver JS object is where you script UI automation.

Behind the scenes this does an eval - and you can omit the eval keyword when calling a method (or setting a property) on it - and when you don't need to save any result using def.

You can refer to the Java interface definition of the driver object to better understand what the various operations are. Note that Map<String, Object> translates to JSON, and JavaBean getters and setters translate to JS properties - e.g. driver.getTitle() becomes driver.title.

driver.location

Get the current URL / address for matching. Example:

Then match driver.location == webUrlBase + '/page-02'

This can also be used as a "setter":

* driver.location = 'http://localhost:8080/test'

driver.title

Get the current page title for matching. Example:

Then match driver.title == 'Test Page'

driver.dimensions

 And driver.dimensions = { left: 0, top: 0, width: 300, height: 800 }

driver.input()

2 string arguments: locator and value to enter.

* driver.input('input[name=someName]', 'test input')

Add a 3rd boolean true argument to clear the input field before entering keystrokes.

* driver.input('input[name=someName]', 'test input', true)

Also see driver.value(locator, value) and driver.clear()

driver.click()

Just triggers a click event on the DOM element, does not wait for a page load.

* driver.click('input[name=someName]')

There is a second rarely used variant which will wait for a JavaScript dialog to appear:

* driver.click('input[name=someName]', true)

driver.submit()

Triggers a click event on the DOM element, and waits for the next page to load.

* driver.submit('.myClass')

driver.select()

Specially for select boxes. There are four variations and use the locator conventions.

# select by displayed text
Given driver.select('select[name=data1]', '^Option Two')

# select by partial displayed text
And driver.select('select[name=data1]', '*Two')

# select by `value`
Given driver.select('select[name=data1]', 'option2')

# select by index
Given driver.select('select[name=data1]', 2)

driver.focus()

* driver.focus('.myClass')

driver.clear()

* driver.clear('#myInput')

If this does not work, try driver.value(selector, value)

driver.close()

Close the page / tab.

driver.quit()

Close the browser.

driver.html()

Get the innerHTML. Example:

And match driver.html('.myClass') == '<span>Class Locator Test</span>'

driver.text()

Get the text-content. Example:

And match driver.text('.myClass') == 'Class Locator Test'

driver.value()

Get the HTML form-element value. Example:

And match driver.value('.myClass') == 'some value'

driver.value(set)

Set the HTML form-element value. Example:

When driver.value('#eg01InputId', 'something more')

driver.attribute()

Get the HTML element attribute value. Example:

And match driver.attribute('#eg01SubmitId', 'type') == 'submit'

driver.waitUntil()

Wait for the JS expression to evaluate to true. Will poll using the retry settings configured.

* driver.waitUntil("document.readyState == 'complete'")

driver.eval()

Will actually attempt to evaluate the given string as JavaScript within the browser.

driver.refresh()

Normal page reload, does not clear cache.

driver.reload()

Hard page reload, after clearing the cache.

driver.back()

driver.forward()

driver.maximize()

driver.minimize()

driver.fullscreen()

driver.cookie

Set a cookie:

Given def cookie2 = { name: 'hello', value: 'world' }
When driver.cookie = cookie2
Then match driver.cookies contains '#(^cookie2)'

driver.cookie()

Get a cookie by name:

* def cookie1 = { name: 'foo', value: 'bar' }
And match driver.cookies contains '#(^cookie1)'
And match driver.cookie('foo') contains cookie1

driver.cookies

See above examples.

driver.deleteCookie()

Delete a cookie by name:

When driver.deleteCookie('foo')
Then match driver.cookies !contains '#(^cookie1)'

driver.clearCookies()

Clear all cookies.

When driver.clearCookies()
Then match driver.cookies == '#[0]'

driver.dialog()

Two forms. The first takes a single boolean argument, whether to "accept" or "cancel". The second form has an additional string argument which is the text to enter for cases where the dialog is expecting user input.

Also works as a "getter" to retrieve the text of the currently visible dialog:

* match driver.dialog == 'Please enter your name:'

driver.switchTo()

When multiple browser tabs are present, allows you to switch to one based on page title (or URL).

When driver.switchTo('Page Two')

driver.screenshot()

Two forms, if a locator is provided only that HTML element will be captured, else the browser viewport will be captured.

driver.pdf()

Only supported for driver type chrome. See Chrome Java API.

driver.highlight()

Useful to visually highlight an element in the browser, especially when working in the Karate UI

* driver.highlight('#eg01DivId')