api-debugger-spec.js 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285
  1. const chai = require('chai')
  2. const dirtyChai = require('dirty-chai')
  3. const http = require('http')
  4. const path = require('path')
  5. const { emittedOnce } = require('./events-helpers')
  6. const { closeWindow } = require('./window-helpers')
  7. const { BrowserWindow } = require('electron').remote
  8. const { expect } = chai
  9. chai.use(dirtyChai)
  10. describe('debugger module', () => {
  11. const fixtures = path.resolve(__dirname, 'fixtures')
  12. let w = null
  13. beforeEach(() => {
  14. w = new BrowserWindow({
  15. show: false,
  16. width: 400,
  17. height: 400
  18. })
  19. })
  20. afterEach(() => closeWindow(w).then(() => { w = null }))
  21. describe('debugger.attach', () => {
  22. it('succeeds when devtools is already open', done => {
  23. w.webContents.on('did-finish-load', () => {
  24. w.webContents.openDevTools()
  25. try {
  26. w.webContents.debugger.attach()
  27. } catch (err) {
  28. done(`unexpected error : ${err}`)
  29. }
  30. expect(w.webContents.debugger.isAttached()).to.be.true()
  31. done()
  32. })
  33. w.webContents.loadFile(path.join(fixtures, 'pages', 'a.html'))
  34. })
  35. it('fails when protocol version is not supported', done => {
  36. try {
  37. w.webContents.debugger.attach('2.0')
  38. } catch (err) {
  39. expect(w.webContents.debugger.isAttached()).to.be.false()
  40. done()
  41. }
  42. })
  43. it('attaches when no protocol version is specified', done => {
  44. try {
  45. w.webContents.debugger.attach()
  46. } catch (err) {
  47. done(`unexpected error : ${err}`)
  48. }
  49. expect(w.webContents.debugger.isAttached()).to.be.true()
  50. done()
  51. })
  52. })
  53. describe('debugger.detach', () => {
  54. it('fires detach event', (done) => {
  55. w.webContents.debugger.on('detach', (e, reason) => {
  56. expect(reason).to.equal('target closed')
  57. expect(w.webContents.debugger.isAttached()).to.be.false()
  58. done()
  59. })
  60. try {
  61. w.webContents.debugger.attach()
  62. } catch (err) {
  63. done(`unexpected error : ${err}`)
  64. }
  65. w.webContents.debugger.detach()
  66. })
  67. it('doesn\'t disconnect an active devtools session', done => {
  68. w.webContents.loadURL('about:blank')
  69. try {
  70. w.webContents.debugger.attach()
  71. } catch (err) {
  72. return done(`unexpected error : ${err}`)
  73. }
  74. w.webContents.openDevTools()
  75. w.webContents.once('devtools-opened', () => {
  76. w.webContents.debugger.detach()
  77. })
  78. w.webContents.debugger.on('detach', (e, reason) => {
  79. expect(w.webContents.debugger.isAttached()).to.be.false()
  80. expect(w.devToolsWebContents.isDestroyed()).to.be.false()
  81. done()
  82. })
  83. })
  84. })
  85. describe('debugger.sendCommand', () => {
  86. let server
  87. afterEach(() => {
  88. if (server != null) {
  89. server.close()
  90. server = null
  91. }
  92. })
  93. it('returns response', async () => {
  94. w.webContents.loadURL('about:blank')
  95. w.webContents.debugger.attach()
  96. const params = { 'expression': '4+2' }
  97. const res = await w.webContents.debugger.sendCommand('Runtime.evaluate', params)
  98. expect(res.wasThrown).to.be.undefined()
  99. expect(res.result.value).to.equal(6)
  100. w.webContents.debugger.detach()
  101. })
  102. // TODO(miniak): remove when promisification is complete
  103. it('returns response (callback)', done => {
  104. w.webContents.loadURL('about:blank')
  105. try {
  106. w.webContents.debugger.attach()
  107. } catch (err) {
  108. return done(`unexpected error : ${err}`)
  109. }
  110. const callback = (err, res) => {
  111. expect(err).to.be.null()
  112. expect(res.wasThrown).to.be.undefined()
  113. expect(res.result.value).to.equal(6)
  114. w.webContents.debugger.detach()
  115. done()
  116. }
  117. const params = { 'expression': '4+2' }
  118. w.webContents.debugger.sendCommand('Runtime.evaluate', params, callback)
  119. })
  120. it('returns response when devtools is opened', async () => {
  121. w.webContents.loadURL('about:blank')
  122. w.webContents.debugger.attach()
  123. w.webContents.openDevTools()
  124. await emittedOnce(w.webContents, 'devtools-opened')
  125. const params = { 'expression': '4+2' }
  126. const res = await w.webContents.debugger.sendCommand('Runtime.evaluate', params)
  127. expect(res.wasThrown).to.be.undefined()
  128. expect(res.result.value).to.equal(6)
  129. w.webContents.debugger.detach()
  130. })
  131. // TODO(miniak): remove when promisification is complete
  132. it('returns response when devtools is opened (callback)', done => {
  133. w.webContents.loadURL('about:blank')
  134. try {
  135. w.webContents.debugger.attach()
  136. } catch (err) {
  137. return done(`unexpected error : ${err}`)
  138. }
  139. const callback = (err, res) => {
  140. expect(err).to.be.null()
  141. expect(res.wasThrown).to.be.undefined()
  142. expect(res.result.value).to.equal(6)
  143. w.webContents.debugger.detach()
  144. done()
  145. }
  146. w.webContents.openDevTools()
  147. w.webContents.once('devtools-opened', () => {
  148. const params = { 'expression': '4+2' }
  149. w.webContents.debugger.sendCommand('Runtime.evaluate', params, callback)
  150. })
  151. })
  152. it('fires message event', done => {
  153. const url = process.platform !== 'win32'
  154. ? `file://${path.join(fixtures, 'pages', 'a.html')}`
  155. : `file:///${path.join(fixtures, 'pages', 'a.html').replace(/\\/g, '/')}`
  156. w.webContents.loadFile(path.join(fixtures, 'pages', 'a.html'))
  157. try {
  158. w.webContents.debugger.attach()
  159. } catch (err) {
  160. done(`unexpected error : ${err}`)
  161. }
  162. w.webContents.debugger.on('message', (e, method, params) => {
  163. if (method === 'Console.messageAdded') {
  164. expect(params.message.level).to.equal('log')
  165. expect(params.message.url).to.equal(url)
  166. expect(params.message.text).to.equal('a')
  167. w.webContents.debugger.detach()
  168. done()
  169. }
  170. })
  171. w.webContents.debugger.sendCommand('Console.enable')
  172. })
  173. it('returns error message when command fails', async () => {
  174. w.webContents.loadURL('about:blank')
  175. w.webContents.debugger.attach()
  176. const promise = w.webContents.debugger.sendCommand('Test')
  177. await expect(promise).to.be.eventually.rejectedWith(Error, "'Test' wasn't found")
  178. w.webContents.debugger.detach()
  179. })
  180. // TODO(miniak): remove when promisification is complete
  181. it('returns error message when command fails (callback)', done => {
  182. w.webContents.loadURL('about:blank')
  183. try {
  184. w.webContents.debugger.attach()
  185. } catch (err) {
  186. done(`unexpected error : ${err}`)
  187. }
  188. w.webContents.debugger.sendCommand('Test', (err, res) => {
  189. expect(err).to.be.an.instanceOf(Error).with.property('message', "'Test' wasn't found")
  190. w.webContents.debugger.detach()
  191. done()
  192. })
  193. })
  194. it('handles valid unicode characters in message', (done) => {
  195. try {
  196. w.webContents.debugger.attach()
  197. } catch (err) {
  198. done(`unexpected error : ${err}`)
  199. }
  200. w.webContents.debugger.on('message', (event, method, params) => {
  201. if (method === 'Network.loadingFinished') {
  202. w.webContents.debugger.sendCommand('Network.getResponseBody', {
  203. requestId: params.requestId
  204. }, (_, data) => {
  205. expect(data.body).to.equal('\u0024')
  206. done()
  207. })
  208. }
  209. })
  210. server = http.createServer((req, res) => {
  211. res.setHeader('Content-Type', 'text/plain; charset=utf-8')
  212. res.end('\u0024')
  213. })
  214. server.listen(0, '127.0.0.1', () => {
  215. w.webContents.debugger.sendCommand('Network.enable')
  216. w.loadURL(`http://127.0.0.1:${server.address().port}`)
  217. })
  218. })
  219. it('does not crash for invalid unicode characters in message', (done) => {
  220. try {
  221. w.webContents.debugger.attach()
  222. } catch (err) {
  223. done(`unexpected error : ${err}`)
  224. }
  225. w.webContents.debugger.on('message', (event, method, params) => {
  226. // loadingFinished indicates that page has been loaded and it did not
  227. // crash because of invalid UTF-8 data
  228. if (method === 'Network.loadingFinished') {
  229. done()
  230. }
  231. })
  232. server = http.createServer((req, res) => {
  233. res.setHeader('Content-Type', 'text/plain; charset=utf-8')
  234. res.end('\uFFFF')
  235. })
  236. server.listen(0, '127.0.0.1', () => {
  237. w.webContents.debugger.sendCommand('Network.enable')
  238. w.loadURL(`http://127.0.0.1:${server.address().port}`)
  239. })
  240. })
  241. })
  242. })