blob: 89f26c822b2c353abf7f0b2078244887a7179ed4 [file] [log] [blame]
<!DOCTYPE HTML>
<html>
<script src="test.js"></script>
<script src="call_function.js"></script>
<script>
function wrapArgs(value, w3c) {
if (typeof w3c === "undefined") {
w3c = true;
}
const nodes = [];
const preprocessed = preprocessResult(value, [], nodes);
const stringified = JSON.stringify(preprocessed);
return [JSON.parse(stringified), w3c, nodes];
}
function unwrapResult(value) {
return resolveReferences(JSON.parse(value[0]), value.slice(1));
}
function serialize(value) {
const nodes = [];
const preprocessed = preprocessResult(value, [], nodes);
return [JSON.stringify(preprocessed), ...nodes];
}
function deserialize(serialized) {
return resolveReferences(JSON.parse(serialized[0]), serialized.slice(1));
}
function roundtrip(value) {
return deserialize(serialize(value));
}
function testSerialize() {
assertEquals(1, roundtrip(1));
assertEquals("1", roundtrip("1"));
assertEquals("chromium", roundtrip("chromium"));
assertEquals(false, roundtrip(false));
assertEquals(null, roundtrip(null));
assertEquals(undefined, roundtrip(undefined));
assertEquals(JSON.stringify([]), JSON.stringify(roundtrip([])));
assertEquals(JSON.stringify(["uno", 2]), JSON.stringify(roundtrip(["uno", 2])));
assertEquals(JSON.stringify({}), JSON.stringify(roundtrip({})));
assertEquals(JSON.stringify({}),
JSON.stringify(roundtrip(function(){console.log("Hello, World!");})));
const div = document.querySelector("div");
assertEquals(div, roundtrip(div));
assertEquals(document, roundtrip(document));
const divs = document.querySelectorAll("div");
const deserialized = roundtrip(divs);
assertEquals(divs[0], deserialized[0]);
assertEquals(divs[1], deserialized[1]);
}
function testDeepSerialization() {
const original = [1, new Array(1, new Object({a: 1, b: {a: 1, b: {}, c: 3}}), 3)];
const originalJson = JSON.stringify(original);
original[1][1].b.b = document;
let deserialized = roundtrip(original);
assertEquals(document, deserialized[1][1].b.b);
const div = document.querySelector("div");
original[1][1].b.b = div;
deserialized = roundtrip(original);
assertEquals(div, deserialized[1][1].b.b);
deserialized[1][1].b.b = {};
assertEquals(originalJson, JSON.stringify(deserialized));
}
function testObjectWithLengthProperty() {
let obj = {length: 200};
assertEquals(200, roundtrip({length: 200})["length"])
assertEquals(JSON.stringify(obj), JSON.stringify(roundtrip({length: 200})))
obj = {bar: "foo", bazz: {length: 150}};
const deserialized = roundtrip(obj);
assertEquals("foo", deserialized["bar"])
assertEquals(150, deserialized["bazz"]["length"])
assertEquals(JSON.stringify(obj), JSON.stringify(deserialized));
}
function testDeserializeElementReference() {
const original = {};
original[ELEMENT_KEY] = 0;
const json = JSON.stringify(original);
deserialized = deserialize([json, document]);
assertEquals(deserialized, document);
}
function testDeserializeNegativeIndexThrows() {
const original = {};
original[ELEMENT_KEY] = -1;
const json = JSON.stringify(original);
let thrown = false;
try {
deserialize([json, document]);
} catch (e) {
thrown = true;
}
assert(thrown);
}
function testDeserializeIndexOutOfRange() {
const original = {};
original[ELEMENT_KEY] = 1;
const json = JSON.stringify(original);
let thrown = false;
try {
deserialize([json, document]);
} catch (e) {
thrown = true;
}
assert(thrown);
}
function testStaleRef() {
const img = document.createElement("img");
document.body.appendChild(img);
const serialized = serialize(img);
document.body.removeChild(img);
let thrown = false;
try {
deserialize(serialized);
} catch (e) {
thrown = true;
assertEquals(StatusCode.STALE_ELEMENT_REFERENCE, e.code);
}
assert(thrown);
}
function testDeserializeWithNodeIndexOutOfRange() {
const img = document.createElement("img");
document.body.appendChild(img);
const serialized = [serialize(img)[0]];
let thrown = false;
try {
deserialize(serialized);
} catch (e) {
thrown = true;
assertEquals(StatusCode.JAVA_SCRIPT_ERROR, e.code);
}
assert(thrown);
}
function testSerializationWithShadowDomAttached() {
// Set up something in the shadow DOM.
const host = document.body.appendChild(document.createElement("div"));
const root = host.attachShadow({ mode: "open" });
const shadowDiv = root.appendChild(document.createElement("div"));
// Test with attached element in shadow DOM.
const deserialized = roundtrip(shadowDiv);
assertEquals(shadowDiv, deserialized);
document.body.removeChild(host);
}
function testSerializeDetachedElementInShadowTree() {
// Set up something in the shadow DOM.
const host = document.body.appendChild(document.createElement("div"));
const root = host.attachShadow({ mode: "open" });
const shadowDiv = root.appendChild(document.createElement("div"));
// Test with detached element in shadow DOM.
root.removeChild(shadowDiv);
let thrown = false;
try {
serialize(shadowDiv);
document.body.removeChild(host);
} catch (e) {
thrown = true;
document.body.removeChild(host);
assertEquals(StatusCode.STALE_ELEMENT_REFERENCE, e.code);
}
assert(thrown)
}
function testDeserializeDetachedElementInShadowTree() {
// Set up something in the shadow DOM.
const host = document.body.appendChild(document.createElement("div"));
const root = host.attachShadow({ mode: "open" });
const shadowDiv = root.appendChild(document.createElement("div"));
// Test with detached element in shadow DOM.
const serialized = serialize(shadowDiv);
root.removeChild(shadowDiv);
let thrown = false;
try {
deserialize(serialized);
document.body.removeChild(host);
} catch (e) {
thrown = true;
document.body.removeChild(host);
assertEquals(StatusCode.STALE_ELEMENT_REFERENCE, e.code);
}
assert(thrown)
}
function testSerializeElementInDetachedShadowTree() {
// Set up something in the shadow DOM.
const host = document.body.appendChild(document.createElement("div"));
const root = host.attachShadow({ mode: "open" });
const shadowDiv = root.appendChild(document.createElement("div"));
// Test with detached element in shadow DOM.
document.body.removeChild(host);
let thrown = false;
try {
serialize(shadowDiv);
} catch (e) {
thrown = true;
assertEquals(StatusCode.STALE_ELEMENT_REFERENCE, e.code);
}
assert(thrown)
}
function testDeserializeElementInDetachedShadowTree() {
// Set up something in the shadow DOM.
const host = document.body.appendChild(document.createElement("div"));
const root = host.attachShadow({ mode: "open" });
const shadowDiv = root.appendChild(document.createElement("div"));
// Test with detached element in shadow DOM.
const serialized = serialize(shadowDiv);
document.body.removeChild(host);
let thrown = false;
try {
deserialize(serialized);
} catch (e) {
thrown = true;
assertEquals(StatusCode.STALE_ELEMENT_REFERENCE, e.code);
}
assert(thrown)
}
function testSerializeDetachedShadowTreeRoot() {
// Set up something in the shadow DOM.
const host = document.body.appendChild(document.createElement("div"));
const root = host.attachShadow({ mode: "open" });
const shadowDiv = root.appendChild(document.createElement("div"));
// Test with detached element in shadow DOM.
document.body.removeChild(host);
let thrown = false;
try {
serialize(root);
} catch (e) {
thrown = true;
assertEquals(StatusCode.DETACHED_SHADOW_ROOT, e.code);
}
assert(thrown)
}
function testDeserializeDetachedShadowTreeRoot() {
// Set up something in the shadow DOM.
const host = document.body.appendChild(document.createElement("div"));
const root = host.attachShadow({ mode: "open" });
const shadowDiv = root.appendChild(document.createElement("div"));
// Test with detached element in shadow DOM.
const serialized = serialize(root);
document.body.removeChild(host);
let thrown = false;
try {
deserialize(serialized);
} catch (e) {
thrown = true;
assertEquals(StatusCode.DETACHED_SHADOW_ROOT, e.code);
}
assert(thrown)
}
// Verify array serialization works when Array.prototype.toJSON is defined.
function testSerializeArrayToJsonProto(runner) {
Array.prototype.toJSON = () => "proto";
const original = [1, 7, "tres"];
const result = roundtrip(original);
delete Array.prototype.toJSON;
assert(result instanceof Array);
assertEquals(result[0], 1);
assertEquals(result[1], 7);
assertEquals(result[2], "tres");
assertEquals(JSON.stringify(original), JSON.stringify(result));
}
function testSerializeArrayToJsonOwn(runner) {
const original = [10, 7, "tres"];
original.toJSON = () => "own";
const result = roundtrip(original);
assertEquals(result, "own");
assertEquals(JSON.stringify(original), JSON.stringify(result));
}
function testSerializeArrayToJsonOwnAndProto(runner) {
Array.prototype.toJSON = () => "proto";
const original = [10, 33, "tres"];
original.toJSON = () => "own";
const result = roundtrip(original);
delete Array.prototype.toJSON;
assertEquals(result, "own");
assertEquals(JSON.stringify(original), JSON.stringify(result));
}
function testDeepSerializationCustomToJson() {
const fancy = [4, 16, 64];
fancy.toJSON = () => "fancy-tojson";
const original = [1, Array(1, new Object({a: [3, 27], b: {a: 1, b: {}, c: fancy}}), 3)];
const originalJson = JSON.stringify(original);
const div = document.querySelector("div");
original[1][1].b.b = div;
Array.prototype.toJSON = () => "proto";
deserialized = roundtrip(original);
delete Array.prototype.toJSON;
assertEquals(div, deserialized[1][1].b.b);
assertEquals("fancy-tojson", deserialized[1][1].b.c);
deserialized[1][1].b.b = {};
assertEquals(originalJson, JSON.stringify(deserialized));
}
function testDeepSerializationCustomToJsonNestedArrays() {
const fancy = [4, [5, 55], 64];
fancy.toJSON = () => "fancy-tojson";
const original = [111, fancy, "third"];
const originalJson = JSON.stringify(original);
Array.prototype.toJSON = () => "proto";
deserialized = roundtrip(original);
delete Array.prototype.toJSON;
assertEquals(111, deserialized[0]);
assertEquals("fancy-tojson", deserialized[1]);
assertEquals("third", deserialized[2]);
assertEquals(originalJson, JSON.stringify(deserialized));
}
function testCallFunctionNoArgs(runner) {
callFunction(function() { return 1; }, [], true, []).then((resultArray) => {
const value = unwrapResult(resultArray).value;
assertEquals(1, value);
runner.continueTesting();
});
runner.waitForAsync();
}
function testCallFunctionThrows(runner) {
let allComplete = 0;
callFunction(function() { throw new Error("fake error"); },
[], true, []).then((result) => {
const unwrapped = unwrapResult(result);
assertEquals(StatusCode.JAVA_SCRIPT_ERROR, unwrapped.status);
assertEquals("fake error", unwrapped.value);
if (allComplete)
runner.continueTesting();
allComplete += 1;
});
callFunction(function() {
const e = new Error("fake error");
e.code = 77;
e.message = "CUSTOM";
throw e;
}, [], true, []).then((result) => {
const unwrapped = unwrapResult(result);
assertEquals(StatusCode.JAVA_SCRIPT_ERROR, unwrapped.status);
const nestedError = JSON.parse(unwrapped.value);
assertEquals(77, nestedError.status);
assertEquals("CUSTOM", nestedError.value);
if (allComplete)
runner.continueTesting();
allComplete += 1;
});
runner.waitForAsync();
}
function testCallWithMalformedIdThrows() {
const original = {};
original[ELEMENT_KEY] = "malformed_id";
callFunction(function(){}, ...wrapArgs([original])).then((resultArray) => {
const unwrapped = unwrapResult(result);
assertEquals(StatusCode.STALE_ELEMENT_REFERENCE, unwrapped.status);
assertEquals("stale element not found", unwrapped.value);
});
}
function testCallFunctionArgs(runner) {
function func(primitive, elem) {
return [primitive, elem.querySelector("div")];
}
callFunction(func, ...wrapArgs([1, document])).then((resultArray) => {
const values = unwrapResult(resultArray).value;
assertEquals(1, values[0]);
assertEquals(document.querySelector("div"), values[1]);
runner.continueTesting();
});
runner.waitForAsync();
}
function testCallFunctionArgsNonW3C(runner) {
function func(elem) {
return elem.querySelector("div");
}
callFunction(func, ...wrapArgs([document], w3c=false)).then((resultArray) => {
const result = unwrapResult(resultArray).value;
assertEquals(document.querySelector("div"), result);
runner.continueTesting();
});
runner.waitForAsync();
}
function testCallFunctionWithShadowHost(runner) {
// Set up something in the shadow DOM.
const host = document.body.appendChild(document.createElement("div"));
const root = host.attachShadow({ mode: "open" });
const shadowDiv = root.appendChild(document.createElement("div"));
function func(element) {
return element;
}
callFunction(func, ...wrapArgs([host])).then((resultArray) => {
const result = unwrapResult(resultArray).value;
assertEquals(host, result);
document.body.removeChild(host);
runner.continueTesting();
});
runner.waitForAsync();
}
function testCallFunctionWithShadowRoot(runner) {
// Set up something in the shadow DOM.
const host = document.body.appendChild(document.createElement("div"));
const root = host.attachShadow({ mode: "open" });
const shadowDiv = root.appendChild(document.createElement("div"));
function func(element) {
return element;
}
// Should handle shadow root as an argument.
callFunction(func, ...wrapArgs([root])).then((resultArray) => {
const result = unwrapResult(resultArray).value;
assertEquals(root, result);
document.body.removeChild(host);
runner.continueTesting();
});
runner.waitForAsync();
}
// Verify callFunction works when Object.prototype has user-defined functions.
// (https://crbug.com/chromedriver/3074)
function testCallWithFunctionInObject(runner) {
Object.prototype.f = () => {};
function func(arg) {
return { bar: arg };
}
callFunction(func, ...wrapArgs([{ foo: 1 }])).then((resultArray) => {
const result = unwrapResult(resultArray).value;
assertEquals(1, result.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 testCallWithArrayToJsonProto(runner) {
Array.prototype.toJSON = () => "[\"testing\"]";
function func() {
return [111, "dos", 3];
}
callFunction(func, ...wrapArgs([])).then((resultArray) => {
const result = unwrapResult(resultArray).value;
assert(result instanceof Array);
assertEquals(result.length, 3);
assertEquals(result[0], 111);
assertEquals(result[1], 'dos');
assertEquals(result[2], 3);
delete Array.prototype.toJSON;
runner.continueTesting();
});
runner.waitForAsync();
}
// Verify array serialization works when own property toJSON is defined.
// (https://crbug.com/chromedriver/4325)
function testCallWithArrayToJsonOwn(runner) {
function func() {
const result = [1, 2, 3];
result.toJSON = () => "[\"testing\"]";
return result;
}
callFunction(func, ...wrapArgs([])).then((resultArray) => {
const result = unwrapResult(resultArray).value;
assertEquals(typeof result, "string");
assertEquals(result, "[\"testing\"]");
runner.continueTesting();
});
runner.waitForAsync();
}
function testCallWithArrayToJsonOwnAndProto(runner) {
Array.prototype.toJSON = () => "proto";
function func() {
const result = [1, 2, 3];
result.toJSON = () => "own";
return result;
}
callFunction(func, ...wrapArgs([])).then((resultArray) => {
const result = unwrapResult(resultArray).value;
assertEquals(typeof result, "string");
assertEquals(result, "own");
delete Array.prototype.toJSON;
runner.continueTesting();
});
runner.waitForAsync();
}
function testCallWithModifiedObjectProto(runner) {
let 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();
}
function testCallFunctionWithLargeData(runner) {
const original = "0".repeat(10e6);
function func(arg) {
return arg;
}
callFunction(func, ...wrapArgs([original])).then((resultArray) => {
const result = unwrapResult(resultArray).value;
assertEquals(result, original);
runner.continueTesting();
});
runner.waitForAsync();
}
</script>
<body>
<div><span>div1</span></div>
<div><span>div2</span></div>
</body>
</html>