api-ipc-renderer-spec.js 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213
  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. beforeEach(() => { contents = webContents.create({}) })
  112. afterEach(() => {
  113. ipcRenderer.removeAllListeners('pong')
  114. contents.destroy()
  115. contents = null
  116. })
  117. const generateSpecs = (description, webPreferences) => {
  118. describe(description, () => {
  119. it('sends message to WebContents', done => {
  120. contents = webContents.create({
  121. preload: path.join(fixtures, 'module', 'preload-ipc-ping-pong.js'),
  122. ...webPreferences
  123. })
  124. const payload = 'Hello World!'
  125. const webContentsId = remote.getCurrentWebContents().id
  126. ipcRenderer.once('pong', (event, data) => {
  127. expect(payload).to.equal(data)
  128. done()
  129. })
  130. contents.once('did-finish-load', () => {
  131. ipcRenderer.sendTo(contents.id, 'ping', webContentsId, payload)
  132. })
  133. contents.loadFile(path.join(fixtures, 'pages', 'base-page.html'))
  134. })
  135. })
  136. }
  137. generateSpecs('without sandbox', {})
  138. generateSpecs('with sandbox', { sandbox: true })
  139. generateSpecs('with contextIsolation', { contextIsolation: true })
  140. generateSpecs('with contextIsolation + sandbox', { contextIsolation: true, sandbox: true })
  141. })
  142. describe('remote listeners', () => {
  143. it('detaches listeners subscribed to destroyed renderers, and shows a warning', (done) => {
  144. w = new BrowserWindow({ show: false })
  145. w.webContents.once('did-finish-load', () => {
  146. w.webContents.once('did-finish-load', () => {
  147. const expectedMessage = [
  148. 'Attempting to call a function in a renderer window that has been closed or released.',
  149. 'Function provided here: remote-event-handler.html:11:33',
  150. 'Remote event names: remote-handler, other-remote-handler'
  151. ].join('\n')
  152. const results = ipcRenderer.sendSync('try-emit-web-contents-event', w.webContents.id, 'remote-handler')
  153. expect(results).to.deep.equal({
  154. warningMessage: expectedMessage,
  155. listenerCountBefore: 2,
  156. listenerCountAfter: 1
  157. })
  158. done()
  159. })
  160. w.webContents.reload()
  161. })
  162. w.loadURL(`file://${path.join(fixtures, 'api', 'remote-event-handler.html')}`)
  163. })
  164. })
  165. it('throws an error when removing all the listeners', () => {
  166. ipcRenderer.on('test-event', () => {})
  167. expect(ipcRenderer.listenerCount('test-event')).to.equal(1)
  168. expect(() => {
  169. ipcRenderer.removeAllListeners()
  170. }).to.throw(/Removing all listeners from ipcRenderer will make Electron internals stop working/)
  171. ipcRenderer.removeAllListeners('test-event')
  172. expect(ipcRenderer.listenerCount('test-event')).to.equal(0)
  173. })
  174. })