api-ipc-renderer-spec.ts 5.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166
  1. import { ipcMain, BrowserWindow } from 'electron/main';
  2. import { expect } from 'chai';
  3. import { once } from 'node:events';
  4. import { closeWindow } from './lib/window-helpers';
  5. describe('ipcRenderer module', () => {
  6. let w: BrowserWindow;
  7. before(async () => {
  8. w = new BrowserWindow({
  9. show: false,
  10. webPreferences: {
  11. nodeIntegration: true,
  12. nodeIntegrationInSubFrames: true,
  13. contextIsolation: false
  14. }
  15. });
  16. await w.loadURL('about:blank');
  17. });
  18. after(async () => {
  19. await closeWindow(w);
  20. w = null as unknown as BrowserWindow;
  21. });
  22. describe('send()', () => {
  23. it('should work when sending an object containing id property', async () => {
  24. const obj = {
  25. id: 1,
  26. name: 'ly'
  27. };
  28. w.webContents.executeJavaScript(`{
  29. const { ipcRenderer } = require('electron')
  30. ipcRenderer.send('message', ${JSON.stringify(obj)})
  31. }`);
  32. const [, received] = await once(ipcMain, 'message');
  33. expect(received).to.deep.equal(obj);
  34. });
  35. it('can send instances of Date as Dates', async () => {
  36. const isoDate = new Date().toISOString();
  37. w.webContents.executeJavaScript(`{
  38. const { ipcRenderer } = require('electron')
  39. ipcRenderer.send('message', new Date(${JSON.stringify(isoDate)}))
  40. }`);
  41. const [, received] = await once(ipcMain, 'message');
  42. expect(received.toISOString()).to.equal(isoDate);
  43. });
  44. it('can send instances of Buffer', async () => {
  45. const data = 'hello';
  46. w.webContents.executeJavaScript(`{
  47. const { ipcRenderer } = require('electron')
  48. ipcRenderer.send('message', Buffer.from(${JSON.stringify(data)}))
  49. }`);
  50. const [, received] = await once(ipcMain, 'message');
  51. expect(received).to.be.an.instanceOf(Uint8Array);
  52. expect(Buffer.from(data).equals(received)).to.be.true();
  53. });
  54. it('throws when sending objects with DOM class prototypes', async () => {
  55. await expect(w.webContents.executeJavaScript(`{
  56. const { ipcRenderer } = require('electron')
  57. ipcRenderer.send('message', document.location)
  58. }`)).to.eventually.be.rejected();
  59. });
  60. it('does not crash when sending external objects', async () => {
  61. await expect(w.webContents.executeJavaScript(`{
  62. const { ipcRenderer } = require('electron')
  63. const http = require('node:http')
  64. const request = http.request({ port: 5000, hostname: '127.0.0.1', method: 'GET', path: '/' })
  65. const stream = request.agent.sockets['127.0.0.1:5000:'][0]._handle._externalStream
  66. ipcRenderer.send('message', stream)
  67. }`)).to.eventually.be.rejected();
  68. });
  69. it('can send objects that both reference the same object', async () => {
  70. w.webContents.executeJavaScript(`{
  71. const { ipcRenderer } = require('electron')
  72. const child = { hello: 'world' }
  73. const foo = { name: 'foo', child: child }
  74. const bar = { name: 'bar', child: child }
  75. const array = [foo, bar]
  76. ipcRenderer.send('message', array, foo, bar, child)
  77. }`);
  78. const child = { hello: 'world' };
  79. const foo = { name: 'foo', child };
  80. const bar = { name: 'bar', child };
  81. const array = [foo, bar];
  82. const [, arrayValue, fooValue, barValue, childValue] = await once(ipcMain, 'message');
  83. expect(arrayValue).to.deep.equal(array);
  84. expect(fooValue).to.deep.equal(foo);
  85. expect(barValue).to.deep.equal(bar);
  86. expect(childValue).to.deep.equal(child);
  87. });
  88. it('can handle cyclic references', async () => {
  89. w.webContents.executeJavaScript(`{
  90. const { ipcRenderer } = require('electron')
  91. const array = [5]
  92. array.push(array)
  93. const child = { hello: 'world' }
  94. child.child = child
  95. ipcRenderer.send('message', array, child)
  96. }`);
  97. const [, arrayValue, childValue] = await once(ipcMain, 'message');
  98. expect(arrayValue[0]).to.equal(5);
  99. expect(arrayValue[1]).to.equal(arrayValue);
  100. expect(childValue.hello).to.equal('world');
  101. expect(childValue.child).to.equal(childValue);
  102. });
  103. });
  104. describe('sendSync()', () => {
  105. it('can be replied to by setting event.returnValue', async () => {
  106. ipcMain.once('echo', (event, msg) => {
  107. event.returnValue = msg;
  108. });
  109. const msg = await w.webContents.executeJavaScript(`new Promise(resolve => {
  110. const { ipcRenderer } = require('electron')
  111. resolve(ipcRenderer.sendSync('echo', 'test'))
  112. })`);
  113. expect(msg).to.equal('test');
  114. });
  115. });
  116. describe('ipcRenderer.on', () => {
  117. it('is not used for internals', async () => {
  118. const result = await w.webContents.executeJavaScript(`
  119. require('electron').ipcRenderer.eventNames()
  120. `);
  121. expect(result).to.deep.equal([]);
  122. });
  123. });
  124. describe('after context is released', () => {
  125. it('throws an exception', async () => {
  126. const error = await w.webContents.executeJavaScript(`(${() => {
  127. const child = window.open('', 'child', 'show=no,nodeIntegration=yes')! as any;
  128. const childIpc = child.require('electron').ipcRenderer;
  129. child.close();
  130. return new Promise(resolve => {
  131. setInterval(() => {
  132. try {
  133. childIpc.send('hello');
  134. } catch (e) {
  135. resolve(e);
  136. }
  137. }, 16);
  138. });
  139. }})()`);
  140. expect(error).to.have.property('message', 'IPC method called after context was released');
  141. });
  142. });
  143. });