api-ipc-renderer-spec.js 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. 'use strict';
  2. const chai = require('chai');
  3. const dirtyChai = require('dirty-chai');
  4. const http = require('http');
  5. const path = require('path');
  6. const { closeWindow } = require('./window-helpers');
  7. const { expect } = chai;
  8. chai.use(dirtyChai);
  9. const { ipcRenderer, remote } = require('electron');
  10. const { ipcMain, webContents, BrowserWindow } = remote;
  11. describe('ipc renderer module', () => {
  12. const fixtures = path.join(__dirname, 'fixtures');
  13. let w = null;
  14. afterEach(() => closeWindow(w).then(() => { w = null; }));
  15. describe('ipc.sender.send', () => {
  16. it('should work when sending an object containing id property', done => {
  17. const obj = {
  18. id: 1,
  19. name: 'ly'
  20. };
  21. ipcRenderer.once('message', (event, message) => {
  22. expect(message).to.deep.equal(obj);
  23. done();
  24. });
  25. ipcRenderer.send('message', obj);
  26. });
  27. it('can send instances of Date', done => {
  28. const currentDate = new Date();
  29. ipcRenderer.once('message', (event, value) => {
  30. expect(value).to.equal(currentDate.toISOString());
  31. done();
  32. });
  33. ipcRenderer.send('message', currentDate);
  34. });
  35. it('can send instances of Buffer', done => {
  36. const buffer = Buffer.from('hello');
  37. ipcRenderer.once('message', (event, message) => {
  38. expect(buffer.equals(message)).to.be.true();
  39. done();
  40. });
  41. ipcRenderer.send('message', buffer);
  42. });
  43. it('can send objects with DOM class prototypes', done => {
  44. ipcRenderer.once('message', (event, value) => {
  45. expect(value.protocol).to.equal('file:');
  46. expect(value.hostname).to.equal('');
  47. done();
  48. });
  49. ipcRenderer.send('message', document.location);
  50. });
  51. it('can send Electron API objects', done => {
  52. const webContents = remote.getCurrentWebContents();
  53. ipcRenderer.once('message', (event, value) => {
  54. expect(value.browserWindowOptions).to.deep.equal(webContents.browserWindowOptions);
  55. done();
  56. });
  57. ipcRenderer.send('message', webContents);
  58. });
  59. it('does not crash on external objects (regression)', done => {
  60. const request = http.request({ port: 5000, hostname: '127.0.0.1', method: 'GET', path: '/' });
  61. const stream = request.agent.sockets['127.0.0.1:5000:'][0]._handle._externalStream;
  62. request.on('error', () => {});
  63. ipcRenderer.once('message', (event, requestValue, externalStreamValue) => {
  64. expect(requestValue.method).to.equal('GET');
  65. expect(requestValue.path).to.equal('/');
  66. expect(externalStreamValue).to.be.null();
  67. done();
  68. });
  69. ipcRenderer.send('message', request, stream);
  70. });
  71. it('can send objects that both reference the same object', done => {
  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.once('message', (event, arrayValue, fooValue, barValue, childValue) => {
  77. expect(arrayValue).to.deep.equal(array);
  78. expect(fooValue).to.deep.equal(foo);
  79. expect(barValue).to.deep.equal(bar);
  80. expect(childValue).to.deep.equal(child);
  81. done();
  82. });
  83. ipcRenderer.send('message', array, foo, bar, child);
  84. });
  85. it('inserts null for cyclic references', done => {
  86. const array = [5];
  87. array.push(array);
  88. const child = { hello: 'world' };
  89. child.child = child;
  90. ipcRenderer.once('message', (event, arrayValue, childValue) => {
  91. expect(arrayValue[0]).to.equal(5);
  92. expect(arrayValue[1]).to.be.null();
  93. expect(childValue.hello).to.equal('world');
  94. expect(childValue.child).to.be.null();
  95. done();
  96. });
  97. ipcRenderer.send('message', array, child);
  98. });
  99. });
  100. describe('ipc.sendSync', () => {
  101. afterEach(() => {
  102. ipcMain.removeAllListeners('send-sync-message');
  103. });
  104. it('can be replied by setting event.returnValue', () => {
  105. const msg = ipcRenderer.sendSync('echo', 'test');
  106. expect(msg).to.equal('test');
  107. });
  108. });
  109. describe('ipcRenderer.sendTo', () => {
  110. let contents = null;
  111. afterEach(() => {
  112. ipcRenderer.removeAllListeners('pong');
  113. contents.destroy();
  114. contents = null;
  115. });
  116. const generateSpecs = (description, webPreferences) => {
  117. describe(description, () => {
  118. it('sends message to WebContents', done => {
  119. contents = webContents.create({
  120. preload: path.join(fixtures, 'module', 'preload-ipc-ping-pong.js'),
  121. ...webPreferences
  122. });
  123. const payload = 'Hello World!';
  124. ipcRenderer.once('pong', (event, data) => {
  125. expect(payload).to.equal(data);
  126. done();
  127. });
  128. contents.once('did-finish-load', () => {
  129. ipcRenderer.sendTo(contents.id, 'ping', payload);
  130. });
  131. contents.loadFile(path.join(fixtures, 'pages', 'base-page.html'));
  132. });
  133. it('sends message to WebContents (channel has special chars)', done => {
  134. contents = webContents.create({
  135. preload: path.join(fixtures, 'module', 'preload-ipc-ping-pong.js'),
  136. ...webPreferences
  137. });
  138. const payload = 'Hello World!';
  139. ipcRenderer.once('pong-æøåü', (event, data) => {
  140. expect(payload).to.equal(data);
  141. done();
  142. });
  143. contents.once('did-finish-load', () => {
  144. ipcRenderer.sendTo(contents.id, 'ping-æøåü', payload);
  145. });
  146. contents.loadFile(path.join(fixtures, 'pages', 'base-page.html'));
  147. });
  148. });
  149. };
  150. generateSpecs('without sandbox', {});
  151. generateSpecs('with sandbox', { sandbox: true });
  152. generateSpecs('with contextIsolation', { contextIsolation: true });
  153. generateSpecs('with contextIsolation + sandbox', { contextIsolation: true, sandbox: true });
  154. });
  155. describe('remote listeners', () => {
  156. it('detaches listeners subscribed to destroyed renderers, and shows a warning', (done) => {
  157. w = new BrowserWindow({
  158. show: false,
  159. webPreferences: {
  160. nodeIntegration: true
  161. }
  162. });
  163. w.webContents.once('did-finish-load', () => {
  164. w.webContents.once('did-finish-load', () => {
  165. const expectedMessage = [
  166. 'Attempting to call a function in a renderer window that has been closed or released.',
  167. 'Function provided here: remote-event-handler.html:11:33',
  168. 'Remote event names: remote-handler, other-remote-handler'
  169. ].join('\n');
  170. const results = ipcRenderer.sendSync('try-emit-web-contents-event', w.webContents.id, 'remote-handler');
  171. expect(results).to.deep.equal({
  172. warningMessage: expectedMessage,
  173. listenerCountBefore: 2,
  174. listenerCountAfter: 1
  175. });
  176. done();
  177. });
  178. w.webContents.reload();
  179. });
  180. w.loadFile(path.join(fixtures, 'api', 'remote-event-handler.html'));
  181. });
  182. });
  183. describe('ipcRenderer.on', () => {
  184. it('is not used for internals', async () => {
  185. w = new BrowserWindow({
  186. show: false,
  187. webPreferences: {
  188. nodeIntegration: true
  189. }
  190. });
  191. await w.loadURL('about:blank');
  192. const script = `require('electron').ipcRenderer.eventNames()`;
  193. const result = await w.webContents.executeJavaScript(script);
  194. expect(result).to.deep.equal([]);
  195. });
  196. });
  197. });