| <!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.Promise'); | |
| goog.require('goog.dom'); | |
| goog.require('goog.events'); | |
| 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; | |
| var frame, frameWin, frameDoc; | |
| function setUpPage() { | |
| goog.testing.TestCase.getActiveTestCase().promiseTimeout = 30000; // 30s | |
| } | |
| 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; | |
| } | |
| 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: 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 testShouldThrowWhenWrappingRecursiveStructure() { | |
| var obj1 = {}; | |
| var obj2 = {}; | |
| obj1['obj2'] = obj2; | |
| obj2['obj1'] = obj1; | |
| assertThrows(goog.partial(bot.inject.wrapValue, obj1)); | |
| } | |
| 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)); | |
| return new goog.Promise(function(loaded) { | |
| goog.events.listenOnce(frame, 'load', loaded); | |
| frameWin.location.reload(); | |
| }).then(function() { | |
| frameDoc = goog.dom.getFrameContentDocument(frame); | |
| assertThrows(goog.partial(bot.inject.cache.getElement, id, frameDoc)); | |
| }); | |
| } | |
| 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() { | |
| return new goog.Promise(function(done) { | |
| bot.inject.executeAsyncScript( | |
| 'window.setTimeout(goog.partial(arguments[0], 1), 10)', | |
| [], 250, done); | |
| }).then(function(result) { | |
| assertTrue(goog.object.containsKey(result, 'status')); | |
| assertEquals(bot.ErrorCode.SUCCESS, result['status']); | |
| assertTrue(goog.object.containsKey(result, 'value')); | |
| assertEquals(1, result['value']); | |
| }); | |
| } | |
| function testShouldBeAbleToExecuteAsyncScriptThatCallsbackSynchronously() { | |
| return new goog.Promise(function(done) { | |
| bot.inject.executeAsyncScript('arguments[0](1)', [], 0, done); | |
| }).then(function(result) { | |
| assertTrue(goog.object.containsKey(result, 'status')); | |
| assertEquals(bot.ErrorCode.SUCCESS, result['status']); | |
| assertTrue(goog.object.containsKey(result, 'value')); | |
| assertEquals(1, result['value']); | |
| }); | |
| } | |
| function testShouldBeAbleToExecuteAsyncFunc() { | |
| return new goog.Promise(function(done) { | |
| bot.inject.executeAsyncScript( | |
| function(callback) { callback(1) }, [], 0, done); | |
| }).then(function(result) { | |
| assertTrue(goog.object.containsKey(result, 'status')); | |
| assertEquals(bot.ErrorCode.SUCCESS, result['status']); | |
| assertTrue(goog.object.containsKey(result, 'value')); | |
| assertEquals(1, result['value']); | |
| }); | |
| } | |
| function testShouldThrowOnExecuteAsyncException() { | |
| return new goog.Promise(function(done) { | |
| bot.inject.executeAsyncScript('nosuchfunc()', [], 0, done); | |
| }).then(function(result) { | |
| assertTrue(goog.object.containsKey(result, 'status')); | |
| assertEquals(bot.ErrorCode.UNKNOWN_ERROR, result['status']); | |
| }); | |
| } | |
| function testShouldTimeoutInExecuteAsync() { | |
| return new goog.Promise(function(done) { | |
| bot.inject.executeAsyncScript('', [], 0, done); | |
| }).then(function(result) { | |
| assertTrue(goog.object.containsKey(result, 'status')); | |
| assertEquals(bot.ErrorCode.SCRIPT_TIMEOUT, result['status']); | |
| }); | |
| } | |
| function testShouldBeAbleToStringifyResult() { | |
| return new goog.Promise(function(done) { | |
| bot.inject.executeAsyncScript('', [], 0, done, true); | |
| }).then(function(jsonResult) { | |
| var result = bot.json.parse(jsonResult); | |
| assertTrue(goog.object.containsKey(result, 'status')); | |
| assertEquals(bot.ErrorCode.SCRIPT_TIMEOUT, result['status']); | |
| }); | |
| } | |
| function testShouldBeAbleToWrapAndUnwrapInExecuteAsyncScript() { | |
| return new goog.Promise(function(done) { | |
| var id = bot.inject.cache.addElement(document.body); | |
| var args = [{}]; | |
| args[0][bot.inject.ELEMENT_KEY] = id; | |
| bot.inject.executeAsyncScript( | |
| 'arguments[1](arguments[0])', args, 0, done); | |
| }).then(function(result) { | |
| 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 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: Don't skip this. | |
| return; | |
| } | |
| return new goog.Promise(function(done) { | |
| 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, done, false, frameWin); | |
| }).then(function(result) { | |
| 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 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: 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() { | |
| return new goog.Promise(function(done) { | |
| bot.inject.executeAsyncScript(function() { | |
| var callback = arguments[arguments.length - 1]; | |
| callback(arguments[0]()); | |
| }, [function() {return 1;}], 0, done, false); | |
| }).then(function(result) { | |
| assertEquals(bot.ErrorCode.SUCCESS, result['status']); | |
| assertEquals(1, result['value']); | |
| }); | |
| } | |
| function testCorrectlyUnwrapsArgsForInjectedScripts() { | |
| if (goog.userAgent.IE) { | |
| // TODO: 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('BODY', value[1]); | |
| } | |
| function testCorrectlyUnwrapsArgsForInjectedAsyncScripts() { | |
| if (goog.userAgent.IE) { | |
| // TODO: Don't skip this. | |
| return; | |
| } | |
| var wrapped = bot.inject.wrapValue(frameDoc.body); | |
| return new goog.Promise(function(done) { | |
| bot.inject.executeAsyncScript(function(element, callback) { | |
| callback([element, element.tagName.toUpperCase()]); | |
| }, [wrapped], 0, done, false, frameWin); | |
| }).then(function(result) { | |
| 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('BODY', value[1]); | |
| }); | |
| } | |
| function testExecuteScriptShouldBeAbleToRecurseOnItself() { | |
| if (goog.userAgent.IE) { | |
| // TODO: 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> |