blob: 7a187868a1ff017c4dcebe59888296a0861a660a [file] [log] [blame]
<!DOCTYPE html>
<!--
Copyright 2010 WebDriver committers
Copyright 2010 Google Inc.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<html>
<head>
<title>Unit Tests for bot.inject</title>
<script src="test_bootstrap.js"></script>
<script type="text/javascript">
goog.require('bot.Error');
goog.require('bot.ErrorCode');
goog.require('bot.inject');
goog.require('bot.inject.cache');
goog.require('bot.json');
goog.require('bot.userAgent');
goog.require('goog.dom');
goog.require('goog.dom.TagName');
goog.require('goog.events');
goog.require('goog.testing.AsyncTestCase');
goog.require('goog.testing.jsunit');
goog.require('goog.userAgent');
goog.require('goog.userAgent.product');
</script>
</head>
<body>
<div style="display:none">
<iframe id="test-frame" src="testdata/blank_page.html"></iframe>
</div>
<script type="text/javascript">
goog.global.MY_GLOBAL_CONSTANT = 123;
// We need to use an AsyncTestCase since a few of the tests require a
// window.setTimeout delay for the conditions under test.
var asyncTestCase = goog.testing.AsyncTestCase.createAndInstall();
var frame, frameWin, frameDoc;
function setUp() {
frame = goog.dom.$('test-frame');
frameWin = goog.dom.getFrameContentWindow(frame);
frameDoc = goog.dom.getFrameContentDocument(frame);
// Force the cache to be recreated on the next access. We cannot "delete"
// it here because IE complains about it being an unsupported operation.
document[bot.inject.cache.CACHE_KEY_] = null;
// Delete the cache in our test frame, if it was created.
frameDoc[bot.inject.cache.CACHE_KEY_] = null;
// Remove any event listeners.
goog.events.removeAll();
}
function testAddWindowToCache() {
var id = bot.inject.cache.addElement(frameWin);
assertEquals(frameWin, bot.inject.cache.getElement(id));
}
function testAddWindowToCacheMultipleTimesReusesIds() {
var id1 = bot.inject.cache.addElement(frameWin);
var id2 = bot.inject.cache.addElement(frameWin);
assertEquals(id1, id2);
}
function testGetElementThrowsIfWindowIsClosed() {
var win = {document: 1, closed: 1};
var id = bot.inject.cache.addElement(win);
assertThrows(goog.partial(bot.inject.cache.getElement, id));
}
function testShouldWrapAndUnwrapWindow() {
var wrapped = bot.inject.wrapValue(frameWin);
assertNotNull(wrapped);
assertNotUndefined(wrapped);
assertTrue(goog.isObject(wrapped));
assertTrue(goog.object.containsKey(wrapped, bot.inject.WINDOW_KEY));
var id = wrapped[bot.inject.WINDOW_KEY];
var res = bot.inject.cache.getElement(id);
assertEquals(frameWin, res);
assertEquals(frameWin, bot.inject.unwrapValue_(wrapped));
}
function testShouldBeAbleToCacheElements() {
var id = bot.inject.cache.addElement(document.body);
var el = bot.inject.cache.getElement(id);
assertEquals(document.body, el);
}
function testShouldReuseExistingIdIfElementAlreadyExistsInTheCache() {
var id1 = bot.inject.cache.addElement(document.body);
var id2 = bot.inject.cache.addElement(document.body);
assertEquals(id1, id2);
}
function testShouldThrowIfElementDoesNotExistInTheCache() {
assertThrows(goog.partial(bot.inject.cache.getElement, 'not-there'));
}
function testShouldDecodeIdsWhenRetrievingFromTheCache() {
var id = bot.inject.cache.addElement(document.body);
id = encodeURIComponent(id);
var el = bot.inject.cache.getElement(id);
assertEquals(document.body, el);
}
function testShouldThrowIfCachedElementIsNoLongerAttachedToTheDom() {
if (goog.userAgent.IE) {
// TODO(user): Don't skip this.
return;
}
var div = document.createElement('DIV');
document.body.appendChild(div);
var id = bot.inject.cache.addElement(div);
assertEquals(div, bot.inject.cache.getElement(id));
document.body.removeChild(div);
assertThrows(goog.partial(bot.inject.cache.getElement, id));
}
function testDoesNotWrapStringBooleansNumbersOrNull() {
assertEquals('foo', bot.inject.wrapValue('foo'));
assertEquals(123, bot.inject.wrapValue(123));
assertTrue(bot.inject.wrapValue(true));
assertNull(bot.inject.wrapValue(null));
}
function testConvertsUndefinedValueToNullForWrapping() {
assertNull(bot.inject.wrapValue(undefined));
}
function testShouldAddElementsToCacheWhenWrapping() {
var wrapped = bot.inject.wrapValue(document.body);
assertNotNull(wrapped);
assertNotUndefined(wrapped);
assertTrue(goog.isObject(wrapped));
assertTrue(goog.object.containsKey(wrapped, bot.inject.ELEMENT_KEY));
var id = wrapped[bot.inject.ELEMENT_KEY];
var el = bot.inject.cache.getElement(id);
assertEquals(document.body, el);
}
function testShouldRecursivelyWrapArrays() {
var unwrapped = [123, 'abc', null, document.body];
var wrapped = bot.inject.wrapValue(unwrapped);
assertEquals(unwrapped.length, wrapped.length);
assertEquals(unwrapped[0], wrapped[0]);
assertEquals(unwrapped[1], wrapped[1]);
assertEquals(unwrapped[2], wrapped[2]);
assertTrue(goog.object.containsKey(wrapped[3], bot.inject.ELEMENT_KEY));
assertEquals(unwrapped[3], bot.inject.cache.getElement(
wrapped[3][bot.inject.ELEMENT_KEY]));
}
function testShouldBeAbleToWrapNodeListsAsArrays() {
var unwrapped = document.getElementsByTagName('body');
var wrapped = bot.inject.wrapValue(unwrapped);
assertTrue('should return an array, but was ' + goog.typeOf(wrapped),
goog.isArray(wrapped));
assertEquals(unwrapped.length, wrapped.length);
assertTrue(goog.object.containsKey(wrapped[0], bot.inject.ELEMENT_KEY));
assertEquals(document.body, bot.inject.cache.getElement(
wrapped[0][bot.inject.ELEMENT_KEY]));
}
function testShouldBeAbleToUnWrapWrappedValues() {
var unwrapped = [123, 'abc', null, document.body];
var wrapped = bot.inject.wrapValue(unwrapped);
var roundTripped = bot.inject.unwrapValue_(wrapped);
assertArrayEquals(unwrapped, roundTripped);
}
function testShouldBeAbleToUnWrapNestedArrays() {
var unwrapped = [123, 'abc', null, [document.body, null,
[document.body]]];
var wrapped = bot.inject.wrapValue(unwrapped);
var roundTripped = bot.inject.unwrapValue_(wrapped);
assertArrayEquals(unwrapped, roundTripped);
}
function testShouldBeAbleToUnWrapNestedObjects() {
var unwrapped = {'foo': {'bar': document.body}};
var wrapped = bot.inject.wrapValue(unwrapped);
var roundTripped = bot.inject.unwrapValue_(wrapped);
assertTrue(goog.object.containsKey(roundTripped, 'foo'));
var foo = roundTripped['foo'];
assertTrue(goog.object.containsKey(foo, 'bar'));
assertEquals(document.body, foo['bar']);
}
function testShouldUnWrapElementsUsingTheGivenDocumentsCache() {
var wrapped = bot.inject.wrapValue(frameDoc.body);
assertNotNull(wrapped);
assertNotUndefined(wrapped);
assertTrue(goog.isObject(wrapped));
assertTrue(goog.object.containsKey(wrapped, bot.inject.ELEMENT_KEY));
// Should not be able to unwrap using our document since the element
// was cached on its ownerDocument.
assertThrows(goog.partial(bot.inject.unwrapValue_, wrapped));
var unwrapped = bot.inject.unwrapValue_(wrapped, frameDoc);
assertEquals(frameDoc.body, unwrapped);
}
function testShouldBeAbleToUnwrapArgumentsExecuteScriptAndWrapResult() {
var id = bot.inject.cache.addElement(document.body);
var args = [{}];
args[0][bot.inject.ELEMENT_KEY] = id;
var result = bot.inject.executeScript(
function() { return arguments[0]; }, args);
assertTrue(goog.object.containsKey(result, 'status'));
assertEquals(bot.ErrorCode.SUCCESS, result['status']);
assertTrue(goog.object.containsKey(result, 'value'));
assertTrue(goog.object.containsKey(result['value'],
bot.inject.ELEMENT_KEY));
assertEquals(document.body, bot.inject.cache.getElement(
result['value'][bot.inject.ELEMENT_KEY]));
}
function testShouldTrapAndReturnWrappedErrorsFromInjectedScripts() {
var result = bot.inject.executeScript(
function() { throw Error('ouch'); }, []);
assertTrue(goog.object.containsKey(result, 'status'));
assertTrue(goog.object.containsKey(result, 'value'));
assertEquals(bot.ErrorCode.UNKNOWN_ERROR, result['status']);
result = result['value'];
assertTrue(goog.object.containsKey(result, 'message'));
assertEquals('ouch', result['message']);
}
function testShouldResetCacheWhenPageIsRefreshed() {
var id = bot.inject.cache.addElement(frameDoc.body);
assertEquals(frameDoc.body, bot.inject.cache.getElement(id, frameDoc));
asyncTestCase.waitForAsync('Waiting for frame to reload');
goog.events.listenOnce(frame, 'load', function() {
asyncTestCase.continueTesting();
frameDoc = goog.dom.getFrameContentDocument(frame);
assertThrows(goog.partial(bot.inject.cache.getElement, id, frameDoc));
});
frameWin.location.reload();
}
function testShouldStringifyResultsWhenAskedToDoSo() {
var result = bot.inject.executeScript(function() {
return arguments[0] + arguments[1];
}, ['abc', 123], true);
assertEquals('string', goog.typeOf(result));
var json = bot.json.parse(result);
assertTrue('No status: ' + result, 'status' in json);
assertTrue('No value: ' + result, 'value' in json);
assertEquals(0, json['status']);
assertEquals('abc123', json['value']);
}
function testShouldStringifyErrorsWhenAskedForStringResults() {
var result = bot.inject.executeScript(function() {
throw new bot.Error(bot.ErrorCode.NO_SUCH_ELEMENT, 'ouch');
}, [], true);
assertEquals('string', goog.typeOf(result));
var json = bot.json.parse(result);
assertTrue('No status: ' + result, 'status' in json);
assertTrue('No value: ' + result, 'value' in json);
assertEquals(bot.ErrorCode.NO_SUCH_ELEMENT, json['status']);
assertTrue('No message: ' + result, 'message' in json['value']);
assertEquals('ouch', json['value']['message']);
}
function testShouldBeAbleToExecuteAsyncScript() {
function onDone(result) {
asyncTestCase.continueTesting();
assertTrue(goog.object.containsKey(result, 'status'));
assertEquals(bot.ErrorCode.SUCCESS, result['status']);
assertTrue(goog.object.containsKey(result, 'value'));
assertEquals(1, result['value']);
}
asyncTestCase.waitForAsync('callback');
bot.inject.executeAsyncScript(
'window.setTimeout(goog.partial(arguments[0], 1), 10)',
[], 250, onDone);
}
function testShouldBeAbleToExecuteAsyncScriptThatCallsbackSynchronously() {
function onDone(result) {
asyncTestCase.continueTesting();
assertTrue(goog.object.containsKey(result, 'status'));
assertEquals(bot.ErrorCode.SUCCESS, result['status']);
assertTrue(goog.object.containsKey(result, 'value'));
assertEquals(1, result['value']);
}
asyncTestCase.waitForAsync('callback');
bot.inject.executeAsyncScript('arguments[0](1)', [], 0, onDone);
}
function testShouldBeAbleToExecuteAsyncFunc() {
function onDone(result) {
asyncTestCase.continueTesting();
assertTrue(goog.object.containsKey(result, 'status'));
assertEquals(bot.ErrorCode.SUCCESS, result['status']);
assertTrue(goog.object.containsKey(result, 'value'));
assertEquals(1, result['value']);
}
asyncTestCase.waitForAsync('callback');
bot.inject.executeAsyncScript(
function(callback) { callback(1) }, [], 0, onDone);
}
function testShouldThrowOnExecuteAsyncException() {
function onDone(result) {
asyncTestCase.continueTesting();
assertTrue(goog.object.containsKey(result, 'status'));
assertEquals(bot.ErrorCode.UNKNOWN_ERROR, result['status']);
}
asyncTestCase.waitForAsync('callback');
bot.inject.executeAsyncScript('nosuchfunc()', [], 0, onDone);
}
function testShouldTimeoutInExecuteAsync() {
function onDone(result) {
asyncTestCase.continueTesting();
assertTrue(goog.object.containsKey(result, 'status'));
assertEquals(bot.ErrorCode.SCRIPT_TIMEOUT, result['status']);
}
asyncTestCase.waitForAsync('callback');
bot.inject.executeAsyncScript('', [], 0, onDone);
}
function testShouldBeAbleToStringifyResult() {
function onDone(jsonResult) {
asyncTestCase.continueTesting();
var result = bot.json.parse(jsonResult);
assertTrue(goog.object.containsKey(result, 'status'));
assertEquals(bot.ErrorCode.SCRIPT_TIMEOUT, result['status']);
}
asyncTestCase.waitForAsync('callback');
bot.inject.executeAsyncScript('', [], 0, onDone, true);
}
function testShouldBeAbleToWrapAndUnwrapInExecuteAsyncScript() {
function onDone(result) {
asyncTestCase.continueTesting();
assertTrue(goog.object.containsKey(result, 'status'));
assertEquals(bot.ErrorCode.SUCCESS, result['status']);
assertTrue(goog.object.containsKey(result, 'value'));
assertTrue(goog.object.containsKey(result['value'],
bot.inject.ELEMENT_KEY));
assertEquals(document.body, bot.inject.cache.getElement(
result['value'][bot.inject.ELEMENT_KEY]));
}
var id = bot.inject.cache.addElement(document.body);
var args = [{}];
args[0][bot.inject.ELEMENT_KEY] = id;
asyncTestCase.waitForAsync('callback');
var result = bot.inject.executeAsyncScript(
'arguments[1](arguments[0])', args, 0, onDone);
}
function testShouldExecuteAsyncScriptsInTheContextOfTheSpecifiedWindow() {
if (goog.userAgent.IE || goog.userAgent.product.SAFARI ||
(goog.userAgent.product.ANDROID &&
!bot.userAgent.isProductVersion(4))) {
// Android prior to Ice Cream Sandwich has a known issue, b/5006213.
// TODO(user): Don't skip this.
return;
}
function onDone(result) {
asyncTestCase.continueTesting();
var jsonResult = bot.json.stringify(result);
assertTrue(jsonResult, goog.object.containsKey(result, 'status'));
assertEquals(jsonResult, bot.ErrorCode.SUCCESS, result['status']);
assertTrue(jsonResult, goog.object.containsKey(result, 'value'));
var value = result['value'];
assertEquals(jsonResult, true, value['this == window']);
assertEquals(jsonResult, false, value['this == top']);
assertEquals(jsonResult, 'undefined',
value['typeof window.MY_GLOBAL_CONSTANT']);
assertEquals(jsonResult, 'number',
value['typeof window.top.MY_GLOBAL_CONSTANT']);
}
asyncTestCase.waitForAsync('callback');
bot.inject.executeAsyncScript(function(callback) {
callback({
'this == window': (this == window),
'this == top': (this == window.top),
'typeof window.MY_GLOBAL_CONSTANT': (typeof MY_GLOBAL_CONSTANT),
'typeof window.top.MY_GLOBAL_CONSTANT':
(typeof window.top.MY_GLOBAL_CONSTANT)
});
}, [], 0, onDone, false, frameWin);
}
function testShouldExecuteScriptsInTheContextOfTheSpecifiedWindow() {
if (goog.userAgent.IE || goog.userAgent.product.SAFARI ||
(goog.userAgent.product.ANDROID &&
!bot.userAgent.isProductVersion(4))) {
// Android prior to Ice Cream Sandwich has a known issue, b/5006213.
// TODO(user): Don't skip this.
return;
}
var result = bot.inject.executeScript(function() {
return {
'this == window': (this == window),
'this == top': (this == window.top),
'typeof window.MY_GLOBAL_CONSTANT': (typeof MY_GLOBAL_CONSTANT),
'typeof window.top.MY_GLOBAL_CONSTANT':
(typeof window.top.MY_GLOBAL_CONSTANT)
};
}, [], false, frameWin);
var jsonResult = bot.json.stringify(result);
assertTrue(jsonResult, goog.object.containsKey(result, 'status'));
assertEquals(jsonResult, bot.ErrorCode.SUCCESS, result['status']);
assertTrue(jsonResult, goog.object.containsKey(result, 'value'));
var value = result['value'];
assertEquals(jsonResult, true, value['this == window']);
assertEquals(jsonResult, false, value['this == top']);
assertEquals(jsonResult, 'undefined',
value['typeof window.MY_GLOBAL_CONSTANT']);
assertEquals(jsonResult, 'number',
value['typeof window.top.MY_GLOBAL_CONSTANT']);
}
function testCorrectlyUnwrapsFunctionArgsForInjectedScripts() {
var result = bot.inject.executeScript(function() {
return arguments[0]();
}, [function() {return 1;}]);
assertEquals(bot.ErrorCode.SUCCESS, result['status']);
assertEquals(1, result['value']);
}
function testCorrectlyUnwrapsFunctionArgsForInjectedAsyncScripts() {
function onDone(result) {
asyncTestCase.continueTesting();
assertEquals(bot.ErrorCode.SUCCESS, result['status']);
assertEquals(1, result['value']);
}
asyncTestCase.waitForAsync('bot.inject.executeAsyncScript');
var result = bot.inject.executeAsyncScript(function() {
var callback = arguments[arguments.length - 1];
callback(arguments[0]());
}, [function() {return 1;}], 0, onDone, false);
}
function testCorrectlyUnwrapsArgsForInjectedScripts() {
if (goog.userAgent.IE) {
// TODO(user): Don't skip this.
return;
}
var wrapped = bot.inject.wrapValue(frameDoc.body);
var result = bot.inject.executeScript(function() {
return [arguments[0], arguments[0].tagName.toUpperCase()];
}, [wrapped], false, frameWin);
assertEquals(bot.ErrorCode.SUCCESS, result['status']);
var value = result['value'];
assertTrue(goog.isArray(value));
assertEquals(2, value.length);
assertTrue(goog.isObject(value[0]));
assertTrue(goog.object.containsKey(value[0], bot.inject.ELEMENT_KEY));
assertEquals(wrapped[bot.inject.ELEMENT_KEY],
value[0][bot.inject.ELEMENT_KEY]);
assertEquals(goog.dom.TagName.BODY, value[1]);
}
function testCorrectlyUnwrapsArgsForInjectedAsyncScripts() {
if (goog.userAgent.IE) {
// TODO(user): Don't skip this.
return;
}
var wrapped = bot.inject.wrapValue(frameDoc.body);
function onDone(result) {
asyncTestCase.continueTesting();
assertEquals(bot.ErrorCode.SUCCESS, result['status']);
var value = result['value'];
assertTrue(goog.isArray(value));
assertEquals(2, value.length);
assertTrue(goog.isObject(value[0]));
assertTrue(goog.object.containsKey(value[0], bot.inject.ELEMENT_KEY));
assertEquals(wrapped[bot.inject.ELEMENT_KEY],
value[0][bot.inject.ELEMENT_KEY]);
assertEquals(goog.dom.TagName.BODY, value[1]);
}
asyncTestCase.waitForAsync('bot.inject.executeAsyncScript');
bot.inject.executeAsyncScript(function(element, callback) {
callback([element, element.tagName.toUpperCase()]);
}, [wrapped], 0, onDone, false, frameWin);
}
function testExecuteScriptShouldBeAbleToRecurseOnItself() {
if (goog.userAgent.IE) {
// TODO(user): Don't skip this.
return;
}
var json = bot.inject.executeScript(bot.inject.executeScript,
['return 1 +2;'], true);
var result = bot.json.parse(json);
assertTrue(json, goog.object.containsKey(result, 'status'));
assertEquals(json, bot.ErrorCode.SUCCESS, result['status']);
assertTrue(json, goog.object.containsKey(result, 'value'));
var innerResult = result['value'];
assertTrue(json, goog.isObject(innerResult));
assertTrue(json, goog.object.containsKey(innerResult, 'status'));
assertEquals(json, bot.ErrorCode.SUCCESS, innerResult['status']);
assertTrue(json, goog.object.containsKey(innerResult, 'value'));
assertEquals(json, 3, innerResult['value']);
}
</script>
</body>
</html>