feat: exposeInMainWorld allow to expose non-object APIs (#26594)
This commit is contained in:
parent
b111bba387
commit
7672aa9525
4 changed files with 313 additions and 13 deletions
|
@ -44,19 +44,19 @@ The `contextBridge` module has the following methods:
|
||||||
### `contextBridge.exposeInMainWorld(apiKey, api)` _Experimental_
|
### `contextBridge.exposeInMainWorld(apiKey, api)` _Experimental_
|
||||||
|
|
||||||
* `apiKey` String - The key to inject the API onto `window` with. The API will be accessible on `window[apiKey]`.
|
* `apiKey` String - The key to inject the API onto `window` with. The API will be accessible on `window[apiKey]`.
|
||||||
* `api` Record<String, any> - Your API object, more information on what this API can be and how it works is available below.
|
* `api` any - Your API, more information on what this API can be and how it works is available below.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
||||||
### API Objects
|
### API
|
||||||
|
|
||||||
The `api` object provided to [`exposeInMainWorld`](#contextbridgeexposeinmainworldapikey-api-experimental) must be an object
|
The `api` provided to [`exposeInMainWorld`](#contextbridgeexposeinmainworldapikey-api-experimental) must be a `Function`, `String`, `Number`, `Array`, `Boolean`, or an object
|
||||||
whose keys are strings and values are a `Function`, `String`, `Number`, `Array`, `Boolean`, or another nested object that meets the same conditions.
|
whose keys are strings and values are a `Function`, `String`, `Number`, `Array`, `Boolean`, or another nested object that meets the same conditions.
|
||||||
|
|
||||||
`Function` values are proxied to the other context and all other values are **copied** and **frozen**. Any data / primitives sent in
|
`Function` values are proxied to the other context and all other values are **copied** and **frozen**. Any data / primitives sent in
|
||||||
the API object become immutable and updates on either side of the bridge do not result in an update on the other side.
|
the API become immutable and updates on either side of the bridge do not result in an update on the other side.
|
||||||
|
|
||||||
An example of a complex API object is shown below:
|
An example of a complex API is shown below:
|
||||||
|
|
||||||
```javascript
|
```javascript
|
||||||
const { contextBridge } = require('electron')
|
const { contextBridge } = require('electron')
|
||||||
|
|
|
@ -8,7 +8,7 @@ const checkContextIsolationEnabled = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
const contextBridge: Electron.ContextBridge = {
|
const contextBridge: Electron.ContextBridge = {
|
||||||
exposeInMainWorld: (key: string, api: Record<string, any>) => {
|
exposeInMainWorld: (key: string, api: any) => {
|
||||||
checkContextIsolationEnabled();
|
checkContextIsolationEnabled();
|
||||||
return binding.exposeAPIInMainWorld(key, api);
|
return binding.exposeAPIInMainWorld(key, api);
|
||||||
}
|
}
|
||||||
|
|
|
@ -513,11 +513,13 @@ v8::MaybeLocal<v8::Object> CreateProxyForAPI(
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ExposeAPIInMainWorld(const std::string& key,
|
void ExposeAPIInMainWorld(v8::Isolate* isolate,
|
||||||
v8::Local<v8::Object> api_object,
|
const std::string& key,
|
||||||
|
v8::Local<v8::Value> api,
|
||||||
gin_helper::Arguments* args) {
|
gin_helper::Arguments* args) {
|
||||||
TRACE_EVENT1("electron", "ContextBridge::ExposeAPIInMainWorld", "key", key);
|
TRACE_EVENT1("electron", "ContextBridge::ExposeAPIInMainWorld", "key", key);
|
||||||
auto* render_frame = GetRenderFrame(api_object);
|
|
||||||
|
auto* render_frame = GetRenderFrame(isolate->GetCurrentContext()->Global());
|
||||||
CHECK(render_frame);
|
CHECK(render_frame);
|
||||||
auto* frame = render_frame->GetWebFrame();
|
auto* frame = render_frame->GetWebFrame();
|
||||||
CHECK(frame);
|
CHECK(frame);
|
||||||
|
@ -539,12 +541,13 @@ void ExposeAPIInMainWorld(const std::string& key,
|
||||||
context_bridge::ObjectCache object_cache;
|
context_bridge::ObjectCache object_cache;
|
||||||
v8::Context::Scope main_context_scope(main_context);
|
v8::Context::Scope main_context_scope(main_context);
|
||||||
|
|
||||||
v8::MaybeLocal<v8::Object> maybe_proxy = CreateProxyForAPI(
|
v8::MaybeLocal<v8::Value> maybe_proxy = PassValueToOtherContext(
|
||||||
api_object, isolated_context, main_context, &object_cache, false, 0);
|
isolated_context, main_context, api, &object_cache, false, 0);
|
||||||
if (maybe_proxy.IsEmpty())
|
if (maybe_proxy.IsEmpty())
|
||||||
return;
|
return;
|
||||||
auto proxy = maybe_proxy.ToLocalChecked();
|
auto proxy = maybe_proxy.ToLocalChecked();
|
||||||
if (!DeepFreeze(proxy, main_context))
|
if (proxy->IsObject() && !proxy->IsTypedArray() &&
|
||||||
|
!DeepFreeze(v8::Local<v8::Object>::Cast(proxy), main_context))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
global.SetReadOnlyNonConfigurable(key, proxy);
|
global.SetReadOnlyNonConfigurable(key, proxy);
|
||||||
|
|
|
@ -104,6 +104,27 @@ describe('contextBridge', () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
it('should proxy numbers', async () => {
|
it('should proxy numbers', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', 123);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
return root.example;
|
||||||
|
});
|
||||||
|
expect(result).to.equal(123);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make global properties read-only', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', 123);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
root.example = 456;
|
||||||
|
return root.example;
|
||||||
|
});
|
||||||
|
expect(result).to.equal(123);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should proxy nested numbers', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
myNumber: 123
|
myNumber: 123
|
||||||
|
@ -129,6 +150,16 @@ describe('contextBridge', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should proxy strings', async () => {
|
it('should proxy strings', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', 'my-words');
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
return root.example;
|
||||||
|
});
|
||||||
|
expect(result).to.equal('my-words');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should proxy nested strings', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
myString: 'my-words'
|
myString: 'my-words'
|
||||||
|
@ -141,6 +172,16 @@ describe('contextBridge', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should proxy arrays', async () => {
|
it('should proxy arrays', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', [123, 'my-words']);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
return [root.example, Array.isArray(root.example)];
|
||||||
|
});
|
||||||
|
expect(result).to.deep.equal([[123, 'my-words'], true]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should proxy nested arrays', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
myArr: [123, 'my-words']
|
myArr: [123, 'my-words']
|
||||||
|
@ -153,6 +194,21 @@ describe('contextBridge', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should make arrays immutable', async () => {
|
it('should make arrays immutable', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', [123, 'my-words']);
|
||||||
|
});
|
||||||
|
const immutable = await callWithBindings((root: any) => {
|
||||||
|
try {
|
||||||
|
root.example.push(456);
|
||||||
|
return false;
|
||||||
|
} catch {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(immutable).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should make nested arrays immutable', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
myArr: [123, 'my-words']
|
myArr: [123, 'my-words']
|
||||||
|
@ -170,6 +226,16 @@ describe('contextBridge', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should proxy booleans', async () => {
|
it('should proxy booleans', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', true);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
return root.example;
|
||||||
|
});
|
||||||
|
expect(result).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should proxy nested booleans', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
myBool: true
|
myBool: true
|
||||||
|
@ -182,6 +248,18 @@ describe('contextBridge', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should proxy promises and resolve with the correct value', async () => {
|
it('should proxy promises and resolve with the correct value', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example',
|
||||||
|
Promise.resolve('i-resolved')
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
return root.example;
|
||||||
|
});
|
||||||
|
expect(result).to.equal('i-resolved');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should proxy nested promises and resolve with the correct value', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
myPromise: Promise.resolve('i-resolved')
|
myPromise: Promise.resolve('i-resolved')
|
||||||
|
@ -194,6 +272,21 @@ describe('contextBridge', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should proxy promises and reject with the correct value', async () => {
|
it('should proxy promises and reject with the correct value', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', Promise.reject(new Error('i-rejected')));
|
||||||
|
});
|
||||||
|
const result = await callWithBindings(async (root: any) => {
|
||||||
|
try {
|
||||||
|
await root.example;
|
||||||
|
return null;
|
||||||
|
} catch (err) {
|
||||||
|
return err;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
expect(result).to.be.an.instanceOf(Error).with.property('message', 'Uncaught Error: i-rejected');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should proxy nested promises and reject with the correct value', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
myPromise: Promise.reject(new Error('i-rejected'))
|
myPromise: Promise.reject(new Error('i-rejected'))
|
||||||
|
@ -249,6 +342,16 @@ describe('contextBridge', () => {
|
||||||
expect(result).to.deep.equal([123, 'help', false, 'promise']);
|
expect(result).to.deep.equal([123, 'help', false, 'promise']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should proxy functions', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', () => 'return-value');
|
||||||
|
});
|
||||||
|
const result = await callWithBindings(async (root: any) => {
|
||||||
|
return root.example();
|
||||||
|
});
|
||||||
|
expect(result).equal('return-value');
|
||||||
|
});
|
||||||
|
|
||||||
it('should proxy methods that are callable multiple times', async () => {
|
it('should proxy methods that are callable multiple times', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
|
@ -299,7 +402,31 @@ describe('contextBridge', () => {
|
||||||
expect(result).to.deep.equal([123, 456, 789, false]);
|
expect(result).to.deep.equal([123, 456, 789, false]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('it should proxy null and undefined correctly', async () => {
|
it('it should proxy null', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', null);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
// Convert to strings as although the context bridge keeps the right value
|
||||||
|
// IPC does not
|
||||||
|
return `${root.example}`;
|
||||||
|
});
|
||||||
|
expect(result).to.deep.equal('null');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should proxy undefined', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', undefined);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
// Convert to strings as although the context bridge keeps the right value
|
||||||
|
// IPC does not
|
||||||
|
return `${root.example}`;
|
||||||
|
});
|
||||||
|
expect(result).to.deep.equal('undefined');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('it should proxy nested null and undefined correctly', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
values: [null, undefined]
|
values: [null, undefined]
|
||||||
|
@ -313,6 +440,19 @@ describe('contextBridge', () => {
|
||||||
expect(result).to.deep.equal(['null', 'undefined']);
|
expect(result).to.deep.equal(['null', 'undefined']);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should proxy symbols', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
const mySymbol = Symbol('unique');
|
||||||
|
const isSymbol = (s: Symbol) => s === mySymbol;
|
||||||
|
contextBridge.exposeInMainWorld('symbol', mySymbol);
|
||||||
|
contextBridge.exposeInMainWorld('isSymbol', isSymbol);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
return root.isSymbol(root.symbol);
|
||||||
|
});
|
||||||
|
expect(result).to.equal(true, 'symbols should be equal across contexts');
|
||||||
|
});
|
||||||
|
|
||||||
it('should proxy symbols such that symbol equality works', async () => {
|
it('should proxy symbols such that symbol equality works', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
const mySymbol = Symbol('unique');
|
const mySymbol = Symbol('unique');
|
||||||
|
@ -341,6 +481,26 @@ describe('contextBridge', () => {
|
||||||
expect(result).to.equal(123, 'symbols key lookup should work across contexts');
|
expect(result).to.equal(123, 'symbols key lookup should work across contexts');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should proxy typed arrays', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', new Uint8Array(100));
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
return Object.getPrototypeOf(root.example) === Uint8Array.prototype;
|
||||||
|
});
|
||||||
|
expect(result).equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should proxy regexps', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example', /a/g);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
return Object.getPrototypeOf(root.example) === RegExp.prototype;
|
||||||
|
});
|
||||||
|
expect(result).equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
it('should proxy typed arrays and regexps through the serializer', async () => {
|
it('should proxy typed arrays and regexps through the serializer', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
|
@ -466,6 +626,22 @@ describe('contextBridge', () => {
|
||||||
expect(result).to.equal('final value');
|
expect(result).to.equal('final value');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should work with complex nested methods and promises attached directly to the global', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
contextBridge.exposeInMainWorld('example',
|
||||||
|
(second: Function) => second((fourth: Function) => {
|
||||||
|
return fourth();
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings((root: any) => {
|
||||||
|
return root.example((third: Function) => {
|
||||||
|
return third(() => Promise.resolve('final value'));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
expect(result).to.equal('final value');
|
||||||
|
});
|
||||||
|
|
||||||
it('should throw an error when recursion depth is exceeded', async () => {
|
it('should throw an error when recursion depth is exceeded', async () => {
|
||||||
await makeBindingWindow(() => {
|
await makeBindingWindow(() => {
|
||||||
contextBridge.exposeInMainWorld('example', {
|
contextBridge.exposeInMainWorld('example', {
|
||||||
|
@ -641,6 +817,127 @@ describe('contextBridge', () => {
|
||||||
expect(result.protoMatches).to.deep.equal(result.protoMatches.map(() => true));
|
expect(result.protoMatches).to.deep.equal(result.protoMatches.map(() => true));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('should not leak prototypes when attaching directly to the global', async () => {
|
||||||
|
await makeBindingWindow(() => {
|
||||||
|
const toExpose = {
|
||||||
|
number: 123,
|
||||||
|
string: 'string',
|
||||||
|
boolean: true,
|
||||||
|
arr: [123, 'string', true, ['foo']],
|
||||||
|
symbol: Symbol('foo'),
|
||||||
|
bigInt: 10n,
|
||||||
|
getObject: () => ({ thing: 123 }),
|
||||||
|
getNumber: () => 123,
|
||||||
|
getString: () => 'string',
|
||||||
|
getBoolean: () => true,
|
||||||
|
getArr: () => [123, 'string', true, ['foo']],
|
||||||
|
getPromise: async () => ({ number: 123, string: 'string', boolean: true, fn: () => 'string', arr: [123, 'string', true, ['foo']] }),
|
||||||
|
getFunctionFromFunction: async () => () => null,
|
||||||
|
object: {
|
||||||
|
number: 123,
|
||||||
|
string: 'string',
|
||||||
|
boolean: true,
|
||||||
|
arr: [123, 'string', true, ['foo']],
|
||||||
|
getPromise: async () => ({ number: 123, string: 'string', boolean: true, fn: () => 'string', arr: [123, 'string', true, ['foo']] })
|
||||||
|
},
|
||||||
|
receiveArguments: (fn: any) => fn({ key: 'value' }),
|
||||||
|
symbolKeyed: {
|
||||||
|
[Symbol('foo')]: 123
|
||||||
|
}
|
||||||
|
};
|
||||||
|
const keys: string[] = [];
|
||||||
|
Object.entries(toExpose).forEach(([key, value]) => {
|
||||||
|
keys.push(key);
|
||||||
|
contextBridge.exposeInMainWorld(key, value);
|
||||||
|
});
|
||||||
|
contextBridge.exposeInMainWorld('keys', keys);
|
||||||
|
});
|
||||||
|
const result = await callWithBindings(async (root: any) => {
|
||||||
|
const { keys } = root;
|
||||||
|
const cleanedRoot: any = {};
|
||||||
|
for (const [key, value] of Object.entries(root)) {
|
||||||
|
if (keys.includes(key)) {
|
||||||
|
cleanedRoot[key] = value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let arg: any;
|
||||||
|
cleanedRoot.receiveArguments((o: any) => { arg = o; });
|
||||||
|
const protoChecks = [
|
||||||
|
...Object.keys(cleanedRoot).map(key => [key, String]),
|
||||||
|
...Object.getOwnPropertySymbols(cleanedRoot.symbolKeyed).map(key => [key, Symbol]),
|
||||||
|
[cleanedRoot, Object],
|
||||||
|
[cleanedRoot.number, Number],
|
||||||
|
[cleanedRoot.string, String],
|
||||||
|
[cleanedRoot.boolean, Boolean],
|
||||||
|
[cleanedRoot.arr, Array],
|
||||||
|
[cleanedRoot.arr[0], Number],
|
||||||
|
[cleanedRoot.arr[1], String],
|
||||||
|
[cleanedRoot.arr[2], Boolean],
|
||||||
|
[cleanedRoot.arr[3], Array],
|
||||||
|
[cleanedRoot.arr[3][0], String],
|
||||||
|
[cleanedRoot.symbol, Symbol],
|
||||||
|
[cleanedRoot.bigInt, BigInt],
|
||||||
|
[cleanedRoot.getNumber, Function],
|
||||||
|
[cleanedRoot.getNumber(), Number],
|
||||||
|
[cleanedRoot.getObject(), Object],
|
||||||
|
[cleanedRoot.getString(), String],
|
||||||
|
[cleanedRoot.getBoolean(), Boolean],
|
||||||
|
[cleanedRoot.getArr(), Array],
|
||||||
|
[cleanedRoot.getArr()[0], Number],
|
||||||
|
[cleanedRoot.getArr()[1], String],
|
||||||
|
[cleanedRoot.getArr()[2], Boolean],
|
||||||
|
[cleanedRoot.getArr()[3], Array],
|
||||||
|
[cleanedRoot.getArr()[3][0], String],
|
||||||
|
[cleanedRoot.getFunctionFromFunction, Function],
|
||||||
|
[cleanedRoot.getFunctionFromFunction(), Promise],
|
||||||
|
[await cleanedRoot.getFunctionFromFunction(), Function],
|
||||||
|
[cleanedRoot.getPromise(), Promise],
|
||||||
|
[await cleanedRoot.getPromise(), Object],
|
||||||
|
[(await cleanedRoot.getPromise()).number, Number],
|
||||||
|
[(await cleanedRoot.getPromise()).string, String],
|
||||||
|
[(await cleanedRoot.getPromise()).boolean, Boolean],
|
||||||
|
[(await cleanedRoot.getPromise()).fn, Function],
|
||||||
|
[(await cleanedRoot.getPromise()).fn(), String],
|
||||||
|
[(await cleanedRoot.getPromise()).arr, Array],
|
||||||
|
[(await cleanedRoot.getPromise()).arr[0], Number],
|
||||||
|
[(await cleanedRoot.getPromise()).arr[1], String],
|
||||||
|
[(await cleanedRoot.getPromise()).arr[2], Boolean],
|
||||||
|
[(await cleanedRoot.getPromise()).arr[3], Array],
|
||||||
|
[(await cleanedRoot.getPromise()).arr[3][0], String],
|
||||||
|
[cleanedRoot.object, Object],
|
||||||
|
[cleanedRoot.object.number, Number],
|
||||||
|
[cleanedRoot.object.string, String],
|
||||||
|
[cleanedRoot.object.boolean, Boolean],
|
||||||
|
[cleanedRoot.object.arr, Array],
|
||||||
|
[cleanedRoot.object.arr[0], Number],
|
||||||
|
[cleanedRoot.object.arr[1], String],
|
||||||
|
[cleanedRoot.object.arr[2], Boolean],
|
||||||
|
[cleanedRoot.object.arr[3], Array],
|
||||||
|
[cleanedRoot.object.arr[3][0], String],
|
||||||
|
[await cleanedRoot.object.getPromise(), Object],
|
||||||
|
[(await cleanedRoot.object.getPromise()).number, Number],
|
||||||
|
[(await cleanedRoot.object.getPromise()).string, String],
|
||||||
|
[(await cleanedRoot.object.getPromise()).boolean, Boolean],
|
||||||
|
[(await cleanedRoot.object.getPromise()).fn, Function],
|
||||||
|
[(await cleanedRoot.object.getPromise()).fn(), String],
|
||||||
|
[(await cleanedRoot.object.getPromise()).arr, Array],
|
||||||
|
[(await cleanedRoot.object.getPromise()).arr[0], Number],
|
||||||
|
[(await cleanedRoot.object.getPromise()).arr[1], String],
|
||||||
|
[(await cleanedRoot.object.getPromise()).arr[2], Boolean],
|
||||||
|
[(await cleanedRoot.object.getPromise()).arr[3], Array],
|
||||||
|
[(await cleanedRoot.object.getPromise()).arr[3][0], String],
|
||||||
|
[arg, Object],
|
||||||
|
[arg.key, String]
|
||||||
|
];
|
||||||
|
return {
|
||||||
|
protoMatches: protoChecks.map(([a, Constructor]) => Object.getPrototypeOf(a) === Constructor.prototype)
|
||||||
|
};
|
||||||
|
});
|
||||||
|
// Every protomatch should be true
|
||||||
|
expect(result.protoMatches).to.deep.equal(result.protoMatches.map(() => true));
|
||||||
|
});
|
||||||
|
|
||||||
describe('internalContextBridge', () => {
|
describe('internalContextBridge', () => {
|
||||||
describe('overrideGlobalValueFromIsolatedWorld', () => {
|
describe('overrideGlobalValueFromIsolatedWorld', () => {
|
||||||
it('should override top level properties', async () => {
|
it('should override top level properties', async () => {
|
||||||
|
|
Loading…
Reference in a new issue