1
Fork 0
mirror of https://github.com/RGBCube/serenity synced 2025-07-27 15:37:46 +00:00

LibJS: Implement ShadowRealm.prototype.evaluate()

This commit is contained in:
Linus Groh 2021-10-13 21:40:33 +01:00
parent b0ee7f38d0
commit c70784bb82
9 changed files with 260 additions and 1 deletions

View file

@ -0,0 +1,96 @@
describe("normal behavior", () => {
test("length is 1", () => {
expect(ShadowRealm.prototype.evaluate).toHaveLength(1);
});
test("basic functionality", () => {
const shadowRealm = new ShadowRealm();
expect(shadowRealm.evaluate("globalThis.foo = 'bar';")).toBe("bar");
expect(shadowRealm.foo).toBeUndefined();
expect(shadowRealm.evaluate("foo;")).toBe("bar");
expect(shadowRealm.evaluate("foo;")).toBe("bar");
});
test("global object initialization", () => {
// Currently uses a plain JS::GlobalObject, i.e. no TestRunnerGlobalObject functions are available on the
// shadow realm's global object. This may change in the future, update the test accordingly.
const shadowRealm = new ShadowRealm();
expect(shadowRealm.evaluate("globalThis.isStrictMode")).toBeUndefined();
});
test("strict mode behavior", () => {
const shadowRealm = new ShadowRealm();
// NOTE: We don't have access to the isStrictMode() test helper inside the shadow realm, see the comment in the test above.
// sloppy mode
expect(shadowRealm.evaluate("(function() { return !this; })()")).toBe(false);
// strict mode
expect(shadowRealm.evaluate("'use strict'; (function() { return !this; })()")).toBe(true);
// Only the parsed script's strict mode changes the strictEval value used for EvalDeclarationInstantiation
expect(
(function () {
"use strict";
return shadowRealm.evaluate("(function() { return !this; })()");
})()
).toBe(false);
});
test("wrapped function object", () => {
const shadowRealm = new ShadowRealm();
const string = shadowRealm.evaluate("(function () { return 'foo'; })")();
expect(string).toBe("foo");
const wrappedFunction = shadowRealm.evaluate("(function () { return 'foo'; })");
expect(wrappedFunction()).toBe("foo");
expect(typeof wrappedFunction).toBe("function");
expect(Object.getPrototypeOf(wrappedFunction)).toBe(Function.prototype);
expect(() => {
shadowRealm.evaluate("(function () { throw Error(); })")();
}).toThrowWithMessage(
TypeError,
"Call of wrapped target function did not complete normally"
);
});
});
describe("errors", () => {
test("throws for non-string input", () => {
const shadowRealm = new ShadowRealm();
const values = [
[undefined, "undefined"],
[42, "42"],
[new String(), "[object StringObject]"],
];
for (const [value, errorString] of values) {
expect(() => {
shadowRealm.evaluate(value);
}).toThrowWithMessage(TypeError, `${errorString} is not a string`);
}
});
test("throws if non-function object is returned from evaluation", () => {
const shadowRealm = new ShadowRealm();
const values = [
["[]", "[object Array]"],
["({})", "[object Object]"],
["new String()", "[object StringObject]"],
];
for (const [value, errorString] of values) {
expect(() => {
shadowRealm.evaluate(value);
}).toThrowWithMessage(
TypeError,
`Wrapped value must be primitive or a function object, got ${errorString}`
);
}
});
test("any exception is changed to a TypeError", () => {
const shadowRealm = new ShadowRealm();
expect(() => {
shadowRealm.evaluate("(() => { throw 42; })()");
}).toThrowWithMessage(TypeError, "The evaluated script did not complete normally");
});
});