fune/remote/test/browser/browser_runtime_callFunctionOn.js
Victor Porof 991b3c93c6 Bug 1561435 - Format remote/, a=automatic-formatting
# ignore-this-changeset

Differential Revision: https://phabricator.services.mozilla.com/D35925

--HG--
extra : source : b793788d0f38244b33eb59ea36e2c6624dbd12c5
2019-07-05 10:56:48 +02:00

251 lines
8.3 KiB
JavaScript

/* Any copyright is dedicated to the Public Domain.
* http://creativecommons.org/publicdomain/zero/1.0/ */
"use strict";
// Test the Runtime.callFunctionOn
// Also see browser_runtime_evaluate as it covers basic usages of this method.
const TEST_URI = "data:text/html;charset=utf-8,default-test-page";
add_task(async function() {
const { client } = await setupTestForUri(TEST_URI);
const firstContext = await testRuntimeEnable(client);
const contextId = firstContext.id;
await testObjectReferences(client, contextId);
await testExceptions(client, contextId);
await testReturnByValue(client, contextId);
await testAwaitPromise(client, contextId);
await testObjectId(client, contextId);
await client.close();
ok(true, "The client is closed");
BrowserTestUtils.removeTab(gBrowser.selectedTab);
await RemoteAgent.close();
});
async function testRuntimeEnable({ Runtime }) {
// Enable watching for new execution context
await Runtime.enable();
ok(true, "Runtime domain has been enabled");
// Calling Runtime.enable will emit executionContextCreated for the existing contexts
const { context } = await Runtime.executionContextCreated();
ok(!!context.id, "The execution context has an id");
ok(context.auxData.isDefault, "The execution context is the default one");
ok(!!context.auxData.frameId, "The execution context has a frame id set");
return context;
}
async function testObjectReferences({ Runtime }, contextId) {
// First create a JS object remotely via Runtime.evaluate
const { result } = await Runtime.evaluate({
contextId,
expression: "({ foo: 1 })",
});
is(result.type, "object", "The type is correct");
is(result.subtype, null, "The subtype is null for objects");
ok(!!result.objectId, "Got an object id");
// Then increment the `foo` attribute of this JS object, while returning this
// attribute value
const { result: result2 } = await Runtime.callFunctionOn({
executionContextId: contextId,
functionDeclaration: "arg => ++arg.foo",
arguments: [{ objectId: result.objectId }],
});
is(result2.type, "number", "The type is correct");
is(result2.subtype, null, "The subtype is null for numbers");
is(
result2.value,
2,
"Updated the existing object and returned the incremented value"
);
// Finally, try to pass this JS object and get it back. Ensure that it returns
// the same object id. Also increment the attribute again.
const { result: result3 } = await Runtime.callFunctionOn({
executionContextId: contextId,
functionDeclaration: "arg => { arg.foo++; return arg; }",
arguments: [{ objectId: result.objectId }],
});
is(result3.type, "object", "The type is correct");
is(result3.subtype, null, "The subtype is null for objects");
// Remote object are not having unique id. So you may have multiple object ids
// that reference the same remote object
ok(!!result3.objectId, "Got an object id");
isnot(result3.objectId, result.objectId, "The object id is stable");
// Assert that we can still access this object and that its foo attribute
// has been incremented. Use the second object id we got from previous call
// to callFunctionOn.
const { result: result4 } = await Runtime.callFunctionOn({
executionContextId: contextId,
functionDeclaration: "arg => arg.foo",
arguments: [{ objectId: result3.objectId }],
});
is(result4.type, "number", "The type is correct");
is(result4.subtype, null, "The subtype is null for numbers");
is(
result4.value,
3,
"Updated the existing object and returned the incremented value"
);
}
async function testExceptions({ Runtime }, executionContextId) {
// Test error when evaluating the function
let { exceptionDetails } = await Runtime.callFunctionOn({
executionContextId,
functionDeclaration: "doesNotExists()",
});
is(
exceptionDetails.text,
"doesNotExists is not defined",
"Exception message is passed to the client"
);
// Test error when calling the function
({ exceptionDetails } = await Runtime.callFunctionOn({
executionContextId,
functionDeclaration: "() => doesNotExists()",
}));
is(
exceptionDetails.text,
"doesNotExists is not defined",
"Exception message is passed to the client"
);
}
async function testReturnByValue({ Runtime }, executionContextId) {
const values = [
42,
"42",
42.0,
true,
false,
null,
{ foo: true },
{ foo: { bar: 42, str: "str", array: [1, 2, 3] } },
[42, "42", true],
[{ foo: true }],
];
for (const value of values) {
const { result } = await Runtime.callFunctionOn({
executionContextId,
functionDeclaration: "() => (" + JSON.stringify(value) + ")",
returnByValue: true,
});
Assert.deepEqual(
result.value,
value,
"The returned value is the same than the input value"
);
}
// Test undefined individually as JSON.stringify doesn't return a string
const { result } = await Runtime.callFunctionOn({
executionContextId,
functionDeclaration: "() => {}",
returnByValue: true,
});
is(result.value, undefined, "The returned value is undefined");
}
async function testAwaitPromise({ Runtime }, executionContextId) {
// First assert promise resolution with awaitPromise
let { result } = await Runtime.callFunctionOn({
executionContextId,
functionDeclaration: "() => Promise.resolve(42)",
awaitPromise: true,
});
is(result.type, "number", "The type is correct");
is(result.subtype, null, "The subtype is null for numbers");
is(result.value, 42, "The result is the promise's resolution");
// Also test promise rejection with awaitPromise
let { exceptionDetails } = await Runtime.callFunctionOn({
executionContextId,
functionDeclaration: "() => Promise.reject(42)",
awaitPromise: true,
});
is(
exceptionDetails.exception.value,
42,
"The result is the promise's rejection"
);
// Then check delayed promise resolution
({ result } = await Runtime.callFunctionOn({
executionContextId,
functionDeclaration: "() => new Promise(r => setTimeout(() => r(42), 0))",
awaitPromise: true,
}));
is(result.type, "number", "The type is correct");
is(result.subtype, null, "The subtype is null for numbers");
is(result.value, 42, "The result is the promise's resolution");
// And delayed promise rejection
({ exceptionDetails } = await Runtime.callFunctionOn({
executionContextId,
functionDeclaration:
"() => new Promise((_,r) => setTimeout(() => r(42), 0))",
awaitPromise: true,
}));
is(
exceptionDetails.exception.value,
42,
"The result is the promise's rejection"
);
// Finally assert promise resolution without awaitPromise
({ result } = await Runtime.callFunctionOn({
executionContextId,
functionDeclaration: "() => Promise.resolve(42)",
awaitPromise: false,
}));
is(result.type, "object", "The type is correct");
is(result.subtype, "promise", "The subtype is promise");
ok(!!result.objectId, "We got the object id for the promise");
ok(!result.value, "We do not receive any value");
// As well as promise rejection without awaitPromise
({ result } = await Runtime.callFunctionOn({
executionContextId,
functionDeclaration: "() => Promise.reject(42)",
awaitPromise: false,
}));
is(result.type, "object", "The type is correct");
is(result.subtype, "promise", "The subtype is promise");
ok(!!result.objectId, "We got the object id for the promise");
ok(!result.exceptionDetails, "We do not receive any exception");
}
async function testObjectId({ Runtime }, contextId) {
// First create an object via Runtime.evaluate
const { result } = await Runtime.evaluate({
contextId,
expression: "({ foo: 42 })",
});
is(result.type, "object", "The type is correct");
is(result.subtype, null, "The subtype is null for objects");
ok(!!result.objectId, "Got an object id");
// Then apply a method on this object
const { result: result2 } = await Runtime.callFunctionOn({
executionContextId: contextId,
functionDeclaration: "function () { return this.foo; }",
objectId: result.objectId,
});
is(result2.type, "number", "The type is correct");
is(result2.subtype, null, "The subtype is null for numbers");
is(
result2.value,
42,
"We have a good proof that the function was ran against the target object"
);
}