api-debugger-spec.ts 8.3 KB

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