| <!DOCTYPE HTML> |
| <html> |
| <script src='test.js'></script> |
| <script src='call_function.js'></script> |
| <script> |
| function wrap(value) { |
| return jsonSerialize(value, []); |
| } |
| |
| function unwrap(value, opt_cache) { |
| return jsonDeserialize(value, [], opt_cache); |
| } |
| |
| var initialCache = getPageCache().cache_; |
| function clearCache() { |
| getPageCache().cache_ = Object.create(initialCache); |
| } |
| |
| function isValidUUID(uuid) { |
| assertEquals(36, uuid.length); |
| var list = uuid.split("-").map(function(x) {return x.length;}); |
| assertEquals(5, list.length); |
| assertEquals(8, list[0]); |
| assertEquals(4, list[1]); |
| assertEquals(4, list[2]); |
| assertEquals(4, list[3]); |
| assertEquals(12, list[4]); |
| assert(/[a-z0-9-]{36}/.test(uuid)); |
| } |
| |
| function testUUID() { |
| var uuids = {} |
| for (var i = 0; i < 100; i++) { |
| var uuid = generateUUID(); |
| isValidUUID(uuid); |
| assertEquals(null, uuids[uuid]); |
| uuids[uuid] = 1; |
| } |
| } |
| |
| function testCallFunctionNoArgs(runner) { |
| clearCache(); |
| |
| callFunction(function() { return 1; }, []).then((result) => { |
| assertEquals(0, result.status); |
| assertEquals(1, result.value); |
| runner.continueTesting(); |
| }); |
| runner.waitForAsync(); |
| } |
| |
| function testCallFunctionThrows(runner) { |
| clearCache(); |
| let allComplete = 0; |
| callFunction(function() { throw new Error('fake error'); }, |
| []).then((result) => { |
| assertEquals(StatusCode.JAVA_SCRIPT_ERROR, result.status); |
| assertEquals('fake error', result.value); |
| if (allComplete) |
| runner.continueTesting(); |
| allComplete += 1; |
| }); |
| callFunction(function() { |
| var e = new Error('fake error'); |
| e.code = 77; |
| e.message = 'CUSTOM'; |
| throw e; |
| }, []).then((result) => { |
| assertEquals(77, result.status); |
| assertEquals('CUSTOM', result.value); |
| if (allComplete) |
| runner.continueTesting(); |
| allComplete += 1; |
| }); |
| runner.waitForAsync(); |
| } |
| |
| function testCallFunctionArgs(runner) { |
| clearCache(); |
| |
| function func(primitive, elem) { |
| return [primitive, elem.querySelector('div')]; |
| } |
| callFunction(func, [1, wrap(document)]).then((result) => { |
| assertEquals(0, result.status); |
| assertEquals(1, result.value[0]); |
| const cache = getPageCache(); |
| assertEquals(document.querySelector('div'), unwrap(result.value[1], cache)); |
| runner.continueTesting(); |
| }); |
| runner.waitForAsync(); |
| } |
| |
| function testCallFunctionArgsUnwrappedReturn(runner) { |
| clearCache(); |
| |
| function func(elem) { |
| return elem.querySelector('div'); |
| } |
| callFunction(func, [wrap(document)], false, true).then((result) => { |
| assertEquals(document.querySelector('div'), result); |
| runner.continueTesting(); |
| }); |
| runner.waitForAsync(); |
| } |
| |
| function testCacheWrap() { |
| clearCache(); |
| |
| assertEquals(1, wrap(1)); |
| assertEquals(1, unwrap(1)); |
| assertEquals("1", wrap("1")); |
| assertEquals("1", unwrap("1")); |
| assertEquals(false, wrap(false)); |
| assertEquals(false, unwrap(false)); |
| assertEquals(null, wrap(null)); |
| assertEquals(null, unwrap(null)); |
| assertEquals(undefined, wrap(undefined)); |
| assertEquals(undefined, unwrap(undefined)); |
| |
| var cache = getPageCache(); |
| var arr = [1, new Array(1, new Object({a: 1, b: {a: 1, b: {}, c: 3}}), 3)]; |
| var originalJson = JSON.stringify(arr); |
| arr[1][1].b.b[ELEMENT_KEY] = cache.idPrefix_ + '-' + cache.nextId_; |
| var wrappedJson = JSON.stringify(arr); |
| arr[1][1].b.b = document; |
| assertEquals(wrappedJson, JSON.stringify(wrap(arr))); |
| var unwrapped = unwrap(JSON.parse(wrappedJson), cache); |
| assertEquals(document, unwrapped[1][1].b.b); |
| unwrapped[1][1].b.b = {}; |
| assertEquals(originalJson, JSON.stringify(unwrapped)); |
| } |
| |
| function testObjectWithLengthProperty() { |
| clearCache(); |
| |
| let obj = {length: 200}; |
| assertEquals(200, wrap({length: 200})['length']) |
| assertEquals(JSON.stringify(obj), JSON.stringify(wrap({length: 200}))) |
| |
| obj = {bar: 'foo', bazz: {length: 150}}; |
| let wrappedObj = wrap(obj); |
| assertEquals('foo', wrappedObj['bar']) |
| assertEquals(150, wrappedObj['bazz']['length']) |
| assertEquals(JSON.stringify(obj), JSON.stringify(wrappedObj)); |
| |
| let unwrappedObj = unwrap(obj); |
| assertEquals('foo', unwrappedObj['bar']) |
| assertEquals(150, unwrappedObj['bazz']['length']) |
| assertEquals(JSON.stringify(obj), JSON.stringify(unwrappedObj)); |
| } |
| |
| function testCacheDoubleWrap() { |
| clearCache(); |
| |
| assertEquals(wrap(document)[ELEMENT_KEY], wrap(document)[ELEMENT_KEY]); |
| } |
| |
| function testCacheUnwrapThrows() { |
| clearCache(); |
| |
| try { |
| var wrapped = {}; |
| wrapped[ELEMENT_KEY] = '1'; |
| unwrap(wrapped, getPageCache()); |
| assert(false); |
| } catch (e) { |
| } |
| } |
| |
| function testClearStale() { |
| clearCache(); |
| |
| var doc = document; |
| var div = doc.querySelector('div'); |
| var span = doc.querySelector('span'); |
| |
| var wrappedDoc = wrap(doc); |
| var wrappedDiv = wrap(div); |
| var wrappedSpan = wrap(span); |
| |
| var cache = getPageCache(); |
| cache.clearStale(); |
| assertEquals(doc, unwrap(wrappedDoc, cache)); |
| assertEquals(div, unwrap(wrappedDiv, cache)); |
| assertEquals(span, unwrap(wrappedSpan, cache)); |
| |
| div.removeChild(span); |
| cache.clearStale(); |
| assertEquals(doc, unwrap(wrappedDoc, cache)); |
| assertEquals(div, unwrap(wrappedDiv, cache)); |
| try { |
| unwrap(wrappedSpan, cache); |
| assert(false); |
| } catch (e) { |
| } |
| } |
| |
| function testCacheQuerySelector() { |
| clearCache(); |
| |
| var cache = getPageCache(); |
| assertEquals(document.querySelector('div'), |
| unwrap(wrap(document.querySelector('div')), cache)); |
| assertEquals(document.querySelectorAll('div')[0], |
| unwrap(wrap(document.querySelectorAll('div')), cache)[0]); |
| } |
| |
| function testCacheStaleRef() { |
| clearCache(); |
| |
| var cache = getPageCache(); |
| var img = document.createElement('img'); |
| document.body.appendChild(img); |
| var wrappedImg = wrap(img); |
| document.body.removeChild(img); |
| cache.clearStale(); |
| try { |
| unwrap(wrappedImg, cache); |
| assert(false); |
| } catch (e) { |
| assertEquals(StatusCode.STALE_ELEMENT_REFERENCE, e.code); |
| } |
| } |
| |
| function testCallFunctionWithShadowHost(runner) { |
| clearCache(); |
| |
| // Set up something in the shadow DOM. |
| var host = document.body.appendChild(document.createElement('div')); |
| var root = host.attachShadow({ mode: 'open' }); |
| var shadowDiv = root.appendChild(document.createElement('div')); |
| |
| function func(element) { |
| return element; |
| } |
| var wrappedHost = wrap(host); |
| callFunction(func, [wrappedHost]).then((result) => { |
| assertEquals(0, result.status); |
| assertEquals(wrappedHost['ELEMENT'], result.value['ELEMENT']); |
| var cache = getPageCache(); |
| assertEquals(host, unwrap(result.value, cache)); |
| document.body.removeChild(host); |
| runner.continueTesting(); |
| }); |
| runner.waitForAsync(); |
| } |
| |
| function testCallFunctionWithShadowRoot(runner) { |
| clearCache(); |
| |
| // Set up something in the shadow DOM. |
| var host = document.body.appendChild(document.createElement('div')); |
| var root = host.attachShadow({ mode: 'open' }); |
| var shadowDiv = root.appendChild(document.createElement('div')); |
| |
| function func(element) { |
| return element; |
| } |
| var wrappedHost = wrap(host); |
| var wrappedRoot = wrap(root); |
| |
| // Should handle shadow root as an argument. |
| callFunction(func, [wrappedRoot]).then((result) => { |
| assertEquals(0, result.status); |
| assertEquals(wrappedRoot['ELEMENT'], result.value['ELEMENT']); |
| var cache = getPageCache(); |
| assertEquals(root, unwrap(result.value, cache)); |
| |
| document.body.removeChild(host); |
| runner.continueTesting(); |
| }); |
| runner.waitForAsync(); |
| } |
| |
| function testCacheWithShadowDomAttached() { |
| clearCache(); |
| const pageCache = getPageCache(); |
| |
| // Set up something in the shadow DOM. |
| var host = document.body.appendChild(document.createElement('div')); |
| var root = host.attachShadow({ mode: 'open' }); |
| var shadowDiv = root.appendChild(document.createElement('div')); |
| |
| // Test with attached element in shadow DOM. |
| var wrappedDiv = wrap(shadowDiv); |
| pageCache.clearStale(); |
| var unwrappedDiv = unwrap(wrappedDiv, pageCache); |
| assertEquals(shadowDiv, unwrappedDiv); |
| |
| document.body.removeChild(host); |
| } |
| |
| function testCacheWithShadowDomDetachedChild() { |
| clearCache(); |
| |
| // Set up something in the shadow DOM. |
| var host = document.body.appendChild(document.createElement('div')); |
| var root = host.attachShadow({ mode: 'open' }); |
| var shadowDiv = root.appendChild(document.createElement('div')); |
| |
| // Test with detached element in shadow DOM. |
| var wrappedDiv = wrap(shadowDiv); |
| root.removeChild(shadowDiv); |
| var rootCache = getPageCache(); |
| rootCache.clearStale(); |
| try { |
| unwrap(wrappedDiv, rootCache); |
| assert(false); |
| } catch (e) { |
| assertEquals(StatusCode.STALE_ELEMENT_REFERENCE, e.code); |
| } |
| |
| document.body.removeChild(host); |
| } |
| |
| // Verify callFunction works when Object.prototype has user-defined functions. |
| // (https://crbug.com/chromedriver/3074) |
| function testCallWithFunctionInObject(runner) { |
| clearCache(); |
| |
| Object.prototype.f = () => {}; |
| function func(arg) { |
| return { bar: arg }; |
| } |
| callFunction(func, [{ foo: 1 }]).then((result) => { |
| assertEquals(1, result.value.bar.foo); |
| delete Object.prototype.f; |
| runner.continueTesting(); |
| }); |
| runner.waitForAsync(); |
| } |
| |
| // Verify array serialization works when Array.prototype.toJSON is defined. |
| // (https://crbug.com/chromedriver/3084) |
| function testCallWithArrayToJSON(runner) { |
| clearCache(); |
| |
| Array.prototype.toJSON = () => '["testing"]'; |
| function func() { |
| return [1, 2, 3]; |
| } |
| callFunction(func, []).then((result) => { |
| assert(result.value instanceof Array); |
| assertEquals(result.value.length, 3); |
| assertEquals(result.value[2], 3); |
| delete Array.prototype.toJSON; |
| runner.continueTesting(); |
| }); |
| runner.waitForAsync(); |
| } |
| |
| function testCallWithModifiedObjectProto(runner) { |
| var callCount = 0; |
| Object.defineProperty(Object.prototype, 'f', { |
| enumerable: true, |
| configurable: true, |
| get: function() { |
| callCount += 1; |
| } |
| }); |
| |
| callFunction(function() {}, []).then(() => { |
| try { |
| assertEquals(callCount, 0); |
| runner.continueTesting(); |
| } finally { |
| delete Object.prototype.f; |
| } |
| }); |
| runner.waitForAsync(); |
| } |
| </script> |
| <body> |
| <div><span></span></div> |
| </body> |
| </html> |