api-context-bridge-spec.ts 24 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680
  1. import { contextBridge, BrowserWindow, ipcMain } from 'electron'
  2. import { expect } from 'chai'
  3. import * as fs from 'fs-extra'
  4. import * as os from 'os'
  5. import * as path from 'path'
  6. import { closeWindow } from './window-helpers'
  7. import { emittedOnce } from './events-helpers'
  8. const fixturesPath = path.resolve(__dirname, 'fixtures', 'api', 'context-bridge')
  9. describe('contextBridge', () => {
  10. let w: BrowserWindow
  11. let dir: string
  12. afterEach(async () => {
  13. await closeWindow(w)
  14. if (dir) await fs.remove(dir)
  15. })
  16. it('should not be accessible when contextIsolation is disabled', async () => {
  17. w = new BrowserWindow({
  18. show: false,
  19. webPreferences: {
  20. contextIsolation: false,
  21. preload: path.resolve(fixturesPath, 'can-bind-preload.js')
  22. }
  23. })
  24. const [,bound] = await emittedOnce(ipcMain, 'context-bridge-bound', () => w.loadFile(path.resolve(fixturesPath, 'empty.html')))
  25. expect(bound).to.equal(false)
  26. })
  27. it('should be accessible when contextIsolation is enabled', async () => {
  28. w = new BrowserWindow({
  29. show: false,
  30. webPreferences: {
  31. contextIsolation: true,
  32. preload: path.resolve(fixturesPath, 'can-bind-preload.js')
  33. }
  34. })
  35. const [,bound] = await emittedOnce(ipcMain, 'context-bridge-bound', () => w.loadFile(path.resolve(fixturesPath, 'empty.html')))
  36. expect(bound).to.equal(true)
  37. })
  38. const generateTests = (useSandbox: boolean) => {
  39. describe(`with sandbox=${useSandbox}`, () => {
  40. const makeBindingWindow = async (bindingCreator: Function) => {
  41. const preloadContent = `const electron_1 = require('electron');
  42. ${useSandbox ? '' : `require('v8').setFlagsFromString('--expose_gc');
  43. const gc=require('vm').runInNewContext('gc');
  44. electron_1.contextBridge.exposeInMainWorld('GCRunner', {
  45. run: () => gc()
  46. });`}
  47. (${bindingCreator.toString()})();`
  48. const tmpDir = await fs.mkdtemp(path.resolve(os.tmpdir(), 'electron-spec-preload-'))
  49. dir = tmpDir
  50. await fs.writeFile(path.resolve(tmpDir, 'preload.js'), preloadContent)
  51. w = new BrowserWindow({
  52. show: false,
  53. webPreferences: {
  54. contextIsolation: true,
  55. nodeIntegration: true,
  56. sandbox: useSandbox,
  57. preload: path.resolve(tmpDir, 'preload.js')
  58. }
  59. })
  60. await w.loadFile(path.resolve(fixturesPath, 'empty.html'))
  61. }
  62. const callWithBindings = async (fn: Function) => {
  63. return await w.webContents.executeJavaScript(`(${fn.toString()})(window)`)
  64. }
  65. const getGCInfo = async (): Promise<{
  66. functionCount: number
  67. objectCount: number
  68. liveFromValues: number
  69. liveProxyValues: number
  70. }> => {
  71. const [,info] = await emittedOnce(ipcMain, 'gc-info', () => w.webContents.send('get-gc-info'))
  72. return info
  73. }
  74. it('should proxy numbers', async () => {
  75. await makeBindingWindow(() => {
  76. contextBridge.exposeInMainWorld('example', {
  77. myNumber: 123,
  78. })
  79. })
  80. const result = await callWithBindings((root: any) => {
  81. return root.example.myNumber
  82. })
  83. expect(result).to.equal(123)
  84. })
  85. it('should make properties unwriteable', async () => {
  86. await makeBindingWindow(() => {
  87. contextBridge.exposeInMainWorld('example', {
  88. myNumber: 123,
  89. })
  90. })
  91. const result = await callWithBindings((root: any) => {
  92. root.example.myNumber = 456
  93. return root.example.myNumber
  94. })
  95. expect(result).to.equal(123)
  96. })
  97. it('should proxy strings', async () => {
  98. await makeBindingWindow(() => {
  99. contextBridge.exposeInMainWorld('example', {
  100. myString: 'my-words',
  101. })
  102. })
  103. const result = await callWithBindings((root: any) => {
  104. return root.example.myString
  105. })
  106. expect(result).to.equal('my-words')
  107. })
  108. it('should proxy arrays', async () => {
  109. await makeBindingWindow(() => {
  110. contextBridge.exposeInMainWorld('example', {
  111. myArr: [123, 'my-words'],
  112. })
  113. })
  114. const result = await callWithBindings((root: any) => {
  115. return root.example.myArr
  116. })
  117. expect(result).to.deep.equal([123, 'my-words'])
  118. })
  119. it('should make arrays immutable', async () => {
  120. await makeBindingWindow(() => {
  121. contextBridge.exposeInMainWorld('example', {
  122. myArr: [123, 'my-words'],
  123. })
  124. })
  125. const immutable = await callWithBindings((root: any) => {
  126. try {
  127. root.example.myArr.push(456)
  128. return false
  129. } catch {
  130. return true
  131. }
  132. })
  133. expect(immutable).to.equal(true)
  134. })
  135. it('should proxy booleans', async () => {
  136. await makeBindingWindow(() => {
  137. contextBridge.exposeInMainWorld('example', {
  138. myBool: true,
  139. })
  140. })
  141. const result = await callWithBindings((root: any) => {
  142. return root.example.myBool
  143. })
  144. expect(result).to.equal(true)
  145. })
  146. it('should proxy promises and resolve with the correct value', async () => {
  147. await makeBindingWindow(() => {
  148. contextBridge.exposeInMainWorld('example', {
  149. myPromise: Promise.resolve('i-resolved'),
  150. })
  151. })
  152. const result = await callWithBindings(async (root: any) => {
  153. return await root.example.myPromise
  154. })
  155. expect(result).to.equal('i-resolved')
  156. })
  157. it('should proxy promises and reject with the correct value', async () => {
  158. await makeBindingWindow(() => {
  159. contextBridge.exposeInMainWorld('example', {
  160. myPromise: Promise.reject('i-rejected'),
  161. })
  162. })
  163. const result = await callWithBindings(async (root: any) => {
  164. try {
  165. await root.example.myPromise
  166. return null
  167. } catch (err) {
  168. return err
  169. }
  170. })
  171. expect(result).to.equal('i-rejected')
  172. })
  173. it('should proxy promises and resolve with the correct value if it resolves later', async () => {
  174. await makeBindingWindow(() => {
  175. contextBridge.exposeInMainWorld('example', {
  176. myPromise: () => new Promise(r => setTimeout(() => r('delayed'), 20)),
  177. })
  178. })
  179. const result = await callWithBindings(async (root: any) => {
  180. return await root.example.myPromise()
  181. })
  182. expect(result).to.equal('delayed')
  183. })
  184. it('should proxy nested promises correctly', async () => {
  185. await makeBindingWindow(() => {
  186. contextBridge.exposeInMainWorld('example', {
  187. myPromise: () => new Promise(r => setTimeout(() => r(Promise.resolve(123)), 20)),
  188. })
  189. })
  190. const result = await callWithBindings(async (root: any) => {
  191. return await root.example.myPromise()
  192. })
  193. expect(result).to.equal(123)
  194. })
  195. it('should proxy methods', async () => {
  196. await makeBindingWindow(() => {
  197. contextBridge.exposeInMainWorld('example', {
  198. getNumber: () => 123,
  199. getString: () => 'help',
  200. getBoolean: () => false,
  201. getPromise: async () => 'promise'
  202. })
  203. })
  204. const result = await callWithBindings(async (root: any) => {
  205. return [root.example.getNumber(), root.example.getString(), root.example.getBoolean(), await root.example.getPromise()]
  206. })
  207. expect(result).to.deep.equal([123, 'help', false, 'promise'])
  208. })
  209. it('should proxy methods that are callable multiple times', async () => {
  210. await makeBindingWindow(() => {
  211. contextBridge.exposeInMainWorld('example', {
  212. doThing: () => 123
  213. })
  214. })
  215. const result = await callWithBindings(async (root: any) => {
  216. return [root.example.doThing(), root.example.doThing(), root.example.doThing()]
  217. })
  218. expect(result).to.deep.equal([123, 123, 123])
  219. })
  220. it('should proxy methods in the reverse direction', async () => {
  221. await makeBindingWindow(() => {
  222. contextBridge.exposeInMainWorld('example', {
  223. callWithNumber: (fn: any) => fn(123),
  224. })
  225. })
  226. const result = await callWithBindings(async (root: any) => {
  227. return root.example.callWithNumber((n: number) => n + 1)
  228. })
  229. expect(result).to.equal(124)
  230. })
  231. it('should proxy promises in the reverse direction', async () => {
  232. await makeBindingWindow(() => {
  233. contextBridge.exposeInMainWorld('example', {
  234. getPromiseValue: async (p: Promise<any>) => await p,
  235. })
  236. })
  237. const result = await callWithBindings(async (root: any) => {
  238. return await root.example.getPromiseValue(Promise.resolve('my-proxied-value'))
  239. })
  240. expect(result).to.equal('my-proxied-value')
  241. })
  242. it('should proxy objects with number keys', async () => {
  243. await makeBindingWindow(() => {
  244. contextBridge.exposeInMainWorld('example', {
  245. [1]: 123,
  246. [2]: 456,
  247. '3': 789
  248. })
  249. })
  250. const result = await callWithBindings(async (root: any) => {
  251. return [root.example[1], root.example[2], root.example[3], Array.isArray(root.example)]
  252. })
  253. expect(result).to.deep.equal([123, 456, 789, false])
  254. })
  255. it('it should proxy null and undefined correctly', async () => {
  256. await makeBindingWindow(() => {
  257. contextBridge.exposeInMainWorld('example', {
  258. values: [null, undefined]
  259. })
  260. })
  261. const result = await callWithBindings((root: any) => {
  262. // Convert to strings as although the context bridge keeps the right value
  263. // IPC does not
  264. return root.example.values.map((val: any) => `${val}`)
  265. })
  266. expect(result).to.deep.equal(['null', 'undefined'])
  267. })
  268. it('should proxy typed arrays and regexps through the serializer', async () => {
  269. await makeBindingWindow(() => {
  270. contextBridge.exposeInMainWorld('example', {
  271. arr: new Uint8Array(100),
  272. regexp: /a/g
  273. })
  274. })
  275. const result = await callWithBindings((root: any) => {
  276. return [root.example.arr.__proto__ === Uint8Array.prototype, root.example.regexp.__proto__ === RegExp.prototype]
  277. })
  278. expect(result).to.deep.equal([true, true])
  279. })
  280. it('it should handle recursive objects', async () => {
  281. await makeBindingWindow(() => {
  282. const o: any = { value: 135 }
  283. o.o = o
  284. contextBridge.exposeInMainWorld('example', {
  285. o,
  286. })
  287. })
  288. const result = await callWithBindings((root: any) => {
  289. return [root.example.o.value, root.example.o.o.value, root.example.o.o.o.value]
  290. })
  291. expect(result).to.deep.equal([135, 135, 135])
  292. })
  293. it('it should follow expected simple rules of object identity', async () => {
  294. await makeBindingWindow(() => {
  295. const o: any = { value: 135 }
  296. const sub = { thing: 7 }
  297. o.a = sub
  298. o.b = sub
  299. contextBridge.exposeInMainWorld('example', {
  300. o,
  301. })
  302. })
  303. const result = await callWithBindings((root: any) => {
  304. return root.example.a === root.example.b
  305. })
  306. expect(result).to.equal(true)
  307. })
  308. it('it should follow expected complex rules of object identity', async () => {
  309. await makeBindingWindow(() => {
  310. let first: any = null
  311. contextBridge.exposeInMainWorld('example', {
  312. check: (arg: any) => {
  313. if (first === null) {
  314. first = arg
  315. } else {
  316. return first === arg
  317. }
  318. },
  319. })
  320. })
  321. const result = await callWithBindings((root: any) => {
  322. const o = { thing: 123 }
  323. root.example.check(o)
  324. return root.example.check(o)
  325. })
  326. expect(result).to.equal(true)
  327. })
  328. // Can only run tests which use the GCRunner in non-sandboxed environments
  329. if (!useSandbox) {
  330. it('should release the global hold on methods sent across contexts', async () => {
  331. await makeBindingWindow(() => {
  332. require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', (contextBridge as any).debugGC()))
  333. contextBridge.exposeInMainWorld('example', {
  334. getFunction: () => () => 123
  335. })
  336. })
  337. expect((await getGCInfo()).functionCount).to.equal(2)
  338. await callWithBindings(async (root: any) => {
  339. root.x = [root.example.getFunction()]
  340. })
  341. expect((await getGCInfo()).functionCount).to.equal(3)
  342. await callWithBindings(async (root: any) => {
  343. root.x = []
  344. root.GCRunner.run()
  345. })
  346. expect((await getGCInfo()).functionCount).to.equal(2)
  347. })
  348. it('should release the global hold on objects sent across contexts when the object proxy is de-reffed', async () => {
  349. await makeBindingWindow(() => {
  350. require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', (contextBridge as any).debugGC()))
  351. let myObj: any
  352. contextBridge.exposeInMainWorld('example', {
  353. setObj: (o: any) => {
  354. myObj = o
  355. },
  356. getObj: () => myObj
  357. })
  358. })
  359. await callWithBindings(async (root: any) => {
  360. root.GCRunner.run()
  361. })
  362. // Initial Setup
  363. let info = await getGCInfo()
  364. expect(info.liveFromValues).to.equal(3)
  365. expect(info.liveProxyValues).to.equal(3)
  366. expect(info.objectCount).to.equal(6)
  367. // Create Reference
  368. await callWithBindings(async (root: any) => {
  369. root.x = { value: 123 }
  370. root.example.setObj(root.x)
  371. root.GCRunner.run()
  372. })
  373. info = await getGCInfo()
  374. expect(info.liveFromValues).to.equal(4)
  375. expect(info.liveProxyValues).to.equal(4)
  376. expect(info.objectCount).to.equal(8)
  377. // Release Reference
  378. await callWithBindings(async (root: any) => {
  379. root.example.setObj(null)
  380. root.GCRunner.run()
  381. })
  382. info = await getGCInfo()
  383. expect(info.liveFromValues).to.equal(3)
  384. expect(info.liveProxyValues).to.equal(3)
  385. expect(info.objectCount).to.equal(6)
  386. })
  387. it('should release the global hold on objects sent across contexts when the object source is de-reffed', async () => {
  388. await makeBindingWindow(() => {
  389. require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', (contextBridge as any).debugGC()))
  390. let myObj: any;
  391. contextBridge.exposeInMainWorld('example', {
  392. setObj: (o: any) => {
  393. myObj = o
  394. },
  395. getObj: () => myObj
  396. })
  397. })
  398. await callWithBindings(async (root: any) => {
  399. root.GCRunner.run()
  400. })
  401. // Initial Setup
  402. let info = await getGCInfo()
  403. expect(info.liveFromValues).to.equal(3)
  404. expect(info.liveProxyValues).to.equal(3)
  405. expect(info.objectCount).to.equal(6)
  406. // Create Reference
  407. await callWithBindings(async (root: any) => {
  408. root.x = { value: 123 }
  409. root.example.setObj(root.x)
  410. root.GCRunner.run()
  411. })
  412. info = await getGCInfo()
  413. expect(info.liveFromValues).to.equal(4)
  414. expect(info.liveProxyValues).to.equal(4)
  415. expect(info.objectCount).to.equal(8)
  416. // Release Reference
  417. await callWithBindings(async (root: any) => {
  418. delete root.x
  419. root.GCRunner.run()
  420. })
  421. info = await getGCInfo()
  422. expect(info.liveFromValues).to.equal(3)
  423. expect(info.liveProxyValues).to.equal(3)
  424. expect(info.objectCount).to.equal(6)
  425. })
  426. it('should not crash when the object source is de-reffed AND the object proxy is de-reffed', async () => {
  427. await makeBindingWindow(() => {
  428. require('electron').ipcRenderer.on('get-gc-info', e => e.sender.send('gc-info', (contextBridge as any).debugGC()))
  429. let myObj: any;
  430. contextBridge.exposeInMainWorld('example', {
  431. setObj: (o: any) => {
  432. myObj = o
  433. },
  434. getObj: () => myObj
  435. })
  436. })
  437. await callWithBindings(async (root: any) => {
  438. root.GCRunner.run()
  439. })
  440. // Initial Setup
  441. let info = await getGCInfo()
  442. expect(info.liveFromValues).to.equal(3)
  443. expect(info.liveProxyValues).to.equal(3)
  444. expect(info.objectCount).to.equal(6)
  445. // Create Reference
  446. await callWithBindings(async (root: any) => {
  447. root.x = { value: 123 }
  448. root.example.setObj(root.x)
  449. root.GCRunner.run()
  450. })
  451. info = await getGCInfo()
  452. expect(info.liveFromValues).to.equal(4)
  453. expect(info.liveProxyValues).to.equal(4)
  454. expect(info.objectCount).to.equal(8)
  455. // Release Reference
  456. await callWithBindings(async (root: any) => {
  457. delete root.x
  458. root.example.setObj(null)
  459. root.GCRunner.run()
  460. })
  461. info = await getGCInfo()
  462. expect(info.liveFromValues).to.equal(3)
  463. expect(info.liveProxyValues).to.equal(3)
  464. expect(info.objectCount).to.equal(6)
  465. })
  466. }
  467. it('it should not let you overwrite existing exposed things', async () => {
  468. await makeBindingWindow(() => {
  469. let threw = false
  470. contextBridge.exposeInMainWorld('example', {
  471. attempt: 1,
  472. getThrew: () => threw
  473. })
  474. try {
  475. contextBridge.exposeInMainWorld('example', {
  476. attempt: 2,
  477. getThrew: () => threw
  478. })
  479. } catch {
  480. threw = true
  481. }
  482. })
  483. const result = await callWithBindings((root: any) => {
  484. return [root.example.attempt, root.example.getThrew()]
  485. })
  486. expect(result).to.deep.equal([1, true])
  487. })
  488. it('should work with complex nested methods and promises', async () => {
  489. await makeBindingWindow(() => {
  490. contextBridge.exposeInMainWorld('example', {
  491. first: (second: Function) => second(async (fourth: Function) => {
  492. return await fourth()
  493. })
  494. })
  495. })
  496. const result = await callWithBindings((root: any) => {
  497. return root.example.first((third: Function) => {
  498. return third(() => Promise.resolve('final value'))
  499. })
  500. })
  501. expect(result).to.equal('final value')
  502. })
  503. it('should throw an error when recursion depth is exceeded', async () => {
  504. await makeBindingWindow(() => {
  505. contextBridge.exposeInMainWorld('example', {
  506. doThing: (a: any) => console.log(a)
  507. })
  508. })
  509. let threw = await callWithBindings((root: any) => {
  510. try {
  511. let a: any = []
  512. for (let i = 0; i < 999; i++) {
  513. a = [ a ]
  514. }
  515. root.example.doThing(a)
  516. return false
  517. } catch {
  518. return true
  519. }
  520. })
  521. expect(threw).to.equal(false)
  522. threw = await callWithBindings((root: any) => {
  523. try {
  524. let a: any = []
  525. for (let i = 0; i < 1000; i++) {
  526. a = [ a ]
  527. }
  528. root.example.doThing(a)
  529. return false
  530. } catch {
  531. return true
  532. }
  533. })
  534. expect(threw).to.equal(true)
  535. })
  536. it('should not leak prototypes', async () => {
  537. await makeBindingWindow(() => {
  538. contextBridge.exposeInMainWorld('example', {
  539. number: 123,
  540. string: 'string',
  541. boolean: true,
  542. arr: [123, 'string', true, ['foo']],
  543. getNumber: () => 123,
  544. getString: () => 'string',
  545. getBoolean: () => true,
  546. getArr: () => [123, 'string', true, ['foo']],
  547. getPromise: async () => ({ number: 123, string: 'string', boolean: true, fn: () => 'string', arr: [123, 'string', true, ['foo']]}),
  548. getFunctionFromFunction: async () => () => null,
  549. object: {
  550. number: 123,
  551. string: 'string',
  552. boolean: true,
  553. arr: [123, 'string', true, ['foo']],
  554. getPromise: async () => ({ number: 123, string: 'string', boolean: true, fn: () => 'string', arr: [123, 'string', true, ['foo']]}),
  555. },
  556. receiveArguments: (fn: any) => fn({ key: 'value' })
  557. })
  558. })
  559. const result = await callWithBindings(async (root: any) => {
  560. const { example } = root
  561. let arg: any
  562. example.receiveArguments((o: any) => { arg = o })
  563. const protoChecks = [
  564. [example, Object],
  565. [example.number, Number],
  566. [example.string, String],
  567. [example.boolean, Boolean],
  568. [example.arr, Array],
  569. [example.arr[0], Number],
  570. [example.arr[1], String],
  571. [example.arr[2], Boolean],
  572. [example.arr[3], Array],
  573. [example.arr[3][0], String],
  574. [example.getNumber, Function],
  575. [example.getNumber(), Number],
  576. [example.getString(), String],
  577. [example.getBoolean(), Boolean],
  578. [example.getArr(), Array],
  579. [example.getArr()[0], Number],
  580. [example.getArr()[1], String],
  581. [example.getArr()[2], Boolean],
  582. [example.getArr()[3], Array],
  583. [example.getArr()[3][0], String],
  584. [example.getFunctionFromFunction, Function],
  585. [example.getFunctionFromFunction(), Promise],
  586. [await example.getFunctionFromFunction(), Function],
  587. [example.getPromise(), Promise],
  588. [await example.getPromise(), Object],
  589. [(await example.getPromise()).number, Number],
  590. [(await example.getPromise()).string, String],
  591. [(await example.getPromise()).boolean, Boolean],
  592. [(await example.getPromise()).fn, Function],
  593. [(await example.getPromise()).fn(), String],
  594. [(await example.getPromise()).arr, Array],
  595. [(await example.getPromise()).arr[0], Number],
  596. [(await example.getPromise()).arr[1], String],
  597. [(await example.getPromise()).arr[2], Boolean],
  598. [(await example.getPromise()).arr[3], Array],
  599. [(await example.getPromise()).arr[3][0], String],
  600. [example.object, Object],
  601. [example.object.number, Number],
  602. [example.object.string, String],
  603. [example.object.boolean, Boolean],
  604. [example.object.arr, Array],
  605. [example.object.arr[0], Number],
  606. [example.object.arr[1], String],
  607. [example.object.arr[2], Boolean],
  608. [example.object.arr[3], Array],
  609. [example.object.arr[3][0], String],
  610. [await example.object.getPromise(), Object],
  611. [(await example.object.getPromise()).number, Number],
  612. [(await example.object.getPromise()).string, String],
  613. [(await example.object.getPromise()).boolean, Boolean],
  614. [(await example.object.getPromise()).fn, Function],
  615. [(await example.object.getPromise()).fn(), String],
  616. [(await example.object.getPromise()).arr, Array],
  617. [(await example.object.getPromise()).arr[0], Number],
  618. [(await example.object.getPromise()).arr[1], String],
  619. [(await example.object.getPromise()).arr[2], Boolean],
  620. [(await example.object.getPromise()).arr[3], Array],
  621. [(await example.object.getPromise()).arr[3][0], String],
  622. [arg, Object],
  623. [arg.key, String]
  624. ]
  625. return {
  626. protoMatches: protoChecks.map(([a, Constructor]) => a.__proto__ === Constructor.prototype)
  627. }
  628. })
  629. // Every protomatch should be true
  630. expect(result.protoMatches).to.deep.equal(result.protoMatches.map(() => true))
  631. })
  632. })
  633. }
  634. generateTests(true)
  635. generateTests(false)
  636. })