blob: 4152c12d531af4f21379da6bf20e57b618a008a5 [file] [log] [blame]
part of webdriver;
class WebDriver extends _WebDriverBase implements SearchContext {
WebDriver._(commandProcessor) : super('', commandProcessor) ;
/// Creates a WebDriver instance connected to the specified WebDriver server.
static Future<WebDriver> createDriver({
String url: 'http://localhost:4444/wd/hub',
Map<String, dynamic> desiredCapabilities}) {
if (desiredCapabilities == null) {
desiredCapabilities = Capabilities.empty;
}
var client = new HttpClient();
var requestUri = Uri.parse(url + "/session");
return client.openUrl('POST', requestUri).then((req) {
req.followRedirects = false;
req.headers.add(HttpHeaders.ACCEPT, "application/json");
req.headers.contentType = _CONTENT_TYPE_JSON;
var body = UTF8.encode(
JSON.encode({'desiredCapabilities': desiredCapabilities}));
req.contentLength = body.length;
req.add(body);
return req.close();
}).then((rsp) {
// Starting in Selenium 2.34.0, the post/redirect/get pattern is no
// longer used when creating a new session.
if (300 <= rsp.statusCode && rsp.statusCode <= 399) {
return Uri.parse(rsp.headers.value(HttpHeaders.LOCATION));
}
return rsp.transform(new Utf8Decoder())
.fold(new StringBuffer(), (buffer, data) => buffer..write(data))
.then((StringBuffer buffer) {
// Strip NULs that WebDriver seems to include in some responses.
var results = buffer.toString()
.replaceAll(new RegExp('\u{0}*\$'), '');
var status = 0;
// 4xx responses send plain text; others send JSON
if (HttpStatus.BAD_REQUEST <= rsp.statusCode
&& rsp.statusCode < HttpStatus.INTERNAL_SERVER_ERROR) {
status = 13; // UnknownError
if (rsp.statusCode == HttpStatus.NOT_FOUND) {
status = 9; // UnkownCommand
}
throw new WebDriverError(status, results);
}
var respObj = JSON.decode(results);
status = respObj['status'];
if (status != 0) {
var value = respObj['value'];
var message =
value.containsKey('message') ? value['message'] : null;
throw new WebDriverError(status, message);
}
return Uri.parse(url + '/session/' + respObj['sessionId']);
});
}).then((sessionUrl) {
var host = sessionUrl.host;
var port = sessionUrl.port;
if (host.isEmpty) {
host = requestUri.host;
if (port == 0) {
port = requestUri.port;
}
}
CommandProcessor processor = new CommandProcessor(
host, port, sessionUrl.path);
return new WebDriver._(processor);
});
}
/// Navigate to the specified url.
Future<WebDriver> get(String url) =>
_post('url', {'url': url}).then((_) => this);
/// The current url.
Future<String> get currentUrl => _get('url');
/// The title of the current page.
Future<String> get title => _get('title');
/// Search for multiple elements within the entire current page.
@override
Future<List<WebElement>> findElements(By by) => _post('elements', by)
.then((response) =>
response.map((element) =>
new WebElement._(element, _prefix, _commandProcessor))
.toList());
/**
* Search for an element within the entire current page.
*
* Throws [WebDriverError] no such element if a matching element is not found.
*/
Future<WebElement> findElement(By by) => _post('element', by)
.then((element) =>
new WebElement._(element, _prefix, _commandProcessor));
/// An artist's rendition of the current page's source.
Future<String> get pageSource => _get('source');
/// Close the current window, quitting the browser if it is the last window.
Future close() => _delete('window');
/// Quit the browser.
Future quit() => _delete(_prefix);
/// Handles for all of the currently displayed tabs/windows.
Future<List<String>> get windowHandles => _get('window_handles');
/// Handle for the active tab/window.
Future<String> get windowHandle => _get('window_handle');
/**
* The currently focused element, or the body element if no
* element has focus.
*/
Future<WebElement> get activeElement => _get('element/active')
.then((element) {
if (element == null) {
return null;
} else {
new WebElement._(element['ELEMENT'], _prefix, _commandProcessor);
}
});
TargetLocator get switchTo =>
new TargetLocator._(_prefix, _commandProcessor);
Navigation get navigate => new Navigation._(_prefix, _commandProcessor);
Cookies get cookies => new Cookies._(_prefix, _commandProcessor);
Timeouts get timeouts => new Timeouts._(_prefix, _commandProcessor);
Keyboard get keyboard => new Keyboard._(_prefix, _commandProcessor);
Mouse get mouse => new Mouse._(_prefix, _commandProcessor);
Touch get touch => new Touch._(_prefix, _commandProcessor);
Window get window =>
new Window._( 'current', _prefix, _commandProcessor);
Future<List<Window>> get windows => windowHandles
.then((windows) => windows.map((window) =>
new Window._(window, _prefix, _commandProcessor)).toList());
/// Take a screenshot of the current page as PNG.
Future<List<int>> captureScreenshot() => _get('screenshot')
.then((screenshot) => CryptoUtils.base64StringToBytes(screenshot));
/**
* Inject a snippet of JavaScript into the page for execution in the context
* of the currently selected frame. The executed script is assumed to be
* asynchronous and must signal that is done by invoking the provided
* callback, which is always provided as the final argument to the function.
* The value to this callback will be returned to the client.
*
* Asynchronous script commands may not span page loads. If an unload event
* is fired while waiting for a script result, an error will be thrown.
*
* The script argument defines the script to execute in the form of a
* function body. The function will be invoked with the provided args array
* and the values may be accessed via the arguments object in the order
* specified. The final argument will always be a callback function that must
* be invoked to signal that the script has finished.
*
* Arguments may be any JSON-able object. WebElements will be converted to
* the corresponding DOM element. Likewise, any DOM Elements in the script
* result will be converted to WebElements.
*/
Future executeAsync(String script, List args) =>
_post('execute_async', {
'script': script,
'args': args
})
.then(_recursiveElementify);
/**
* Inject a snippet of JavaScript into the page for execution in the context
* of the currently selected frame. The executed script is assumed to be
* synchronous and the result of evaluating the script is returned.
*
* The script argument defines the script to execute in the form of a
* function body. The value returned by that function will be returned to the
* client. The function will be invoked with the provided args array and the
* values may be accessed via the arguments object in the order specified.
*
* Arguments may be any JSON-able object. WebElements will be converted to
* the corresponding DOM element. Likewise, any DOM Elements in the script
* result will be converted to WebElements.
*/
Future execute(String script, List args) =>
_post('execute', {
'script': script,
'args': args
})
.then(_recursiveElementify);
dynamic _recursiveElementify(result) {
if (result is Map) {
if (result.length == 1 && result.containsKey(_ELEMENT)) {
return new WebElement._(result, _prefix, _commandProcessor);
} else {
var newResult = {};
result.forEach((key, value) {
newResult[key] = _recursiveElementify(value);
});
return newResult;
}
} else if (result is List) {
return result.map((value) => _recursiveElementify(value)).toList();
} else {
return result;
}
}
}