node-spec.js 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353
  1. const assert = require('assert')
  2. const ChildProcess = require('child_process')
  3. const fs = require('fs')
  4. const path = require('path')
  5. const os = require('os')
  6. const {ipcRenderer, remote} = require('electron')
  7. const isCI = remote.getGlobal('isCi')
  8. describe('node feature', function () {
  9. var fixtures = path.join(__dirname, 'fixtures')
  10. describe('child_process', function () {
  11. describe('child_process.fork', function () {
  12. it('works in current process', function (done) {
  13. var child = ChildProcess.fork(path.join(fixtures, 'module', 'ping.js'))
  14. child.on('message', function (msg) {
  15. assert.equal(msg, 'message')
  16. done()
  17. })
  18. child.send('message')
  19. })
  20. it('preserves args', function (done) {
  21. var args = ['--expose_gc', '-test', '1']
  22. var child = ChildProcess.fork(path.join(fixtures, 'module', 'process_args.js'), args)
  23. child.on('message', function (msg) {
  24. assert.deepEqual(args, msg.slice(2))
  25. done()
  26. })
  27. child.send('message')
  28. })
  29. it('works in forked process', function (done) {
  30. var child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js'))
  31. child.on('message', function (msg) {
  32. assert.equal(msg, 'message')
  33. done()
  34. })
  35. child.send('message')
  36. })
  37. it('works in forked process when options.env is specifed', function (done) {
  38. var child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js'), [], {
  39. path: process.env['PATH']
  40. })
  41. child.on('message', function (msg) {
  42. assert.equal(msg, 'message')
  43. done()
  44. })
  45. child.send('message')
  46. })
  47. it('works in browser process', function (done) {
  48. var fork = remote.require('child_process').fork
  49. var child = fork(path.join(fixtures, 'module', 'ping.js'))
  50. child.on('message', function (msg) {
  51. assert.equal(msg, 'message')
  52. done()
  53. })
  54. child.send('message')
  55. })
  56. it('has String::localeCompare working in script', function (done) {
  57. var child = ChildProcess.fork(path.join(fixtures, 'module', 'locale-compare.js'))
  58. child.on('message', function (msg) {
  59. assert.deepEqual(msg, [0, -1, 1])
  60. done()
  61. })
  62. child.send('message')
  63. })
  64. it('has setImmediate working in script', function (done) {
  65. var child = ChildProcess.fork(path.join(fixtures, 'module', 'set-immediate.js'))
  66. child.on('message', function (msg) {
  67. assert.equal(msg, 'ok')
  68. done()
  69. })
  70. child.send('message')
  71. })
  72. it('pipes stdio', function (done) {
  73. let child = ChildProcess.fork(path.join(fixtures, 'module', 'process-stdout.js'), {silent: true})
  74. let data = ''
  75. child.stdout.on('data', (chunk) => {
  76. data += String(chunk)
  77. })
  78. child.on('close', (code) => {
  79. assert.equal(code, 0)
  80. assert.equal(data, 'pipes stdio')
  81. done()
  82. })
  83. })
  84. it('works when sending a message to a process forked with the --eval argument', function (done) {
  85. const source = "process.on('message', (message) => { process.send(message) })"
  86. const forked = ChildProcess.fork('--eval', [source])
  87. forked.once('message', (message) => {
  88. assert.equal(message, 'hello')
  89. done()
  90. })
  91. forked.send('hello')
  92. })
  93. })
  94. describe('child_process.spawn', function () {
  95. let child
  96. afterEach(function () {
  97. if (child != null) {
  98. child.kill()
  99. }
  100. })
  101. it('supports spawning Electron as a node process via the ELECTRON_RUN_AS_NODE env var', function (done) {
  102. child = ChildProcess.spawn(process.execPath, [path.join(__dirname, 'fixtures', 'module', 'run-as-node.js')], {
  103. env: {
  104. ELECTRON_RUN_AS_NODE: true
  105. }
  106. })
  107. let output = ''
  108. child.stdout.on('data', function (data) {
  109. output += data
  110. })
  111. child.stdout.on('close', function () {
  112. assert.deepEqual(JSON.parse(output), {
  113. processLog: process.platform === 'win32' ? 'function' : 'undefined',
  114. processType: 'undefined',
  115. window: 'undefined'
  116. })
  117. done()
  118. })
  119. })
  120. it('supports starting the v8 inspector with --inspect/--inspect-brk', function (done) {
  121. child = ChildProcess.spawn(process.execPath, ['--inspect-brk', path.join(__dirname, 'fixtures', 'module', 'run-as-node.js')], {
  122. env: {
  123. ELECTRON_RUN_AS_NODE: true
  124. }
  125. })
  126. let output = ''
  127. child.stderr.on('data', function (data) {
  128. output += data
  129. if (output.trim().startsWith('Debugger listening on port')) {
  130. done()
  131. }
  132. })
  133. child.stdout.on('data', function (data) {
  134. done(new Error(`Unexpected output: ${data.toString()}`))
  135. })
  136. })
  137. })
  138. })
  139. describe('contexts', function () {
  140. describe('setTimeout in fs callback', function () {
  141. if (process.env.TRAVIS === 'true') {
  142. return
  143. }
  144. it('does not crash', function (done) {
  145. fs.readFile(__filename, function () {
  146. setTimeout(done, 0)
  147. })
  148. })
  149. })
  150. describe('error thrown in renderer process node context', function () {
  151. it('gets emitted as a process uncaughtException event', function (done) {
  152. const error = new Error('boo!')
  153. const listeners = process.listeners('uncaughtException')
  154. process.removeAllListeners('uncaughtException')
  155. process.on('uncaughtException', (thrown) => {
  156. assert.strictEqual(thrown, error)
  157. process.removeAllListeners('uncaughtException')
  158. listeners.forEach((listener) => {
  159. process.on('uncaughtException', listener)
  160. })
  161. done()
  162. })
  163. fs.readFile(__filename, () => {
  164. throw error
  165. })
  166. })
  167. })
  168. describe('error thrown in main process node context', function () {
  169. it('gets emitted as a process uncaughtException event', function () {
  170. const error = ipcRenderer.sendSync('handle-uncaught-exception', 'hello')
  171. assert.equal(error, 'hello')
  172. })
  173. })
  174. describe('promise rejection in main process node context', function () {
  175. it('gets emitted as a process unhandledRejection event', function () {
  176. const error = ipcRenderer.sendSync('handle-unhandled-rejection', 'hello')
  177. assert.equal(error, 'hello')
  178. })
  179. })
  180. describe('setTimeout called under Chromium event loop in browser process', function () {
  181. it('can be scheduled in time', function (done) {
  182. remote.getGlobal('setTimeout')(done, 0)
  183. })
  184. })
  185. describe('setInterval called under Chromium event loop in browser process', function () {
  186. it('can be scheduled in time', function (done) {
  187. var clear, interval
  188. clear = function () {
  189. remote.getGlobal('clearInterval')(interval)
  190. done()
  191. }
  192. interval = remote.getGlobal('setInterval')(clear, 10)
  193. })
  194. })
  195. })
  196. describe('message loop', function () {
  197. describe('process.nextTick', function () {
  198. it('emits the callback', function (done) {
  199. process.nextTick(done)
  200. })
  201. it('works in nested calls', function (done) {
  202. process.nextTick(function () {
  203. process.nextTick(function () {
  204. process.nextTick(done)
  205. })
  206. })
  207. })
  208. })
  209. describe('setImmediate', function () {
  210. it('emits the callback', function (done) {
  211. setImmediate(done)
  212. })
  213. it('works in nested calls', function (done) {
  214. setImmediate(function () {
  215. setImmediate(function () {
  216. setImmediate(done)
  217. })
  218. })
  219. })
  220. })
  221. })
  222. describe('net.connect', function () {
  223. if (process.platform !== 'darwin') {
  224. return
  225. }
  226. it('emit error when connect to a socket path without listeners', function (done) {
  227. var socketPath = path.join(os.tmpdir(), 'atom-shell-test.sock')
  228. var script = path.join(fixtures, 'module', 'create_socket.js')
  229. var child = ChildProcess.fork(script, [socketPath])
  230. child.on('exit', function (code) {
  231. assert.equal(code, 0)
  232. var client = require('net').connect(socketPath)
  233. client.on('error', function (error) {
  234. assert.equal(error.code, 'ECONNREFUSED')
  235. done()
  236. })
  237. })
  238. })
  239. })
  240. describe('Buffer', function () {
  241. it('can be created from WebKit external string', function () {
  242. var p = document.createElement('p')
  243. p.innerText = '闲云潭影日悠悠,物换星移几度秋'
  244. var b = new Buffer(p.innerText)
  245. assert.equal(b.toString(), '闲云潭影日悠悠,物换星移几度秋')
  246. assert.equal(Buffer.byteLength(p.innerText), 45)
  247. })
  248. it('correctly parses external one-byte UTF8 string', function () {
  249. var p = document.createElement('p')
  250. p.innerText = 'Jøhänñéß'
  251. var b = new Buffer(p.innerText)
  252. assert.equal(b.toString(), 'Jøhänñéß')
  253. assert.equal(Buffer.byteLength(p.innerText), 13)
  254. })
  255. it('does not crash when creating large Buffers', function () {
  256. var buffer = new Buffer(new Array(4096).join(' '))
  257. assert.equal(buffer.length, 4095)
  258. buffer = new Buffer(new Array(4097).join(' '))
  259. assert.equal(buffer.length, 4096)
  260. })
  261. })
  262. describe('process.stdout', function () {
  263. it('does not throw an exception when accessed', function () {
  264. assert.doesNotThrow(function () {
  265. process.stdout
  266. })
  267. })
  268. it('does not throw an exception when calling write()', function () {
  269. assert.doesNotThrow(function () {
  270. process.stdout.write('test')
  271. })
  272. })
  273. it('should have isTTY defined on Mac and Linux', function () {
  274. if (isCI) return
  275. if (process.platform === 'win32') {
  276. assert.equal(process.stdout.isTTY, undefined)
  277. } else {
  278. assert.equal(typeof process.stdout.isTTY, 'boolean')
  279. }
  280. })
  281. })
  282. describe('process.stdin', function () {
  283. it('does not throw an exception when accessed', function () {
  284. assert.doesNotThrow(function () {
  285. process.stdin
  286. })
  287. })
  288. it('returns null when read from', function () {
  289. assert.equal(process.stdin.read(), null)
  290. })
  291. })
  292. describe('process.version', function () {
  293. it('should not have -pre', function () {
  294. assert(!process.version.endsWith('-pre'))
  295. })
  296. })
  297. describe('vm.createContext', function () {
  298. it('should not crash', function () {
  299. require('vm').runInNewContext('')
  300. })
  301. })
  302. it('includes the electron version in process.versions', () => {
  303. assert(/^\d+\.\d+\.\d+$/.test(process.versions.electron))
  304. })
  305. it('includes the chrome version in process.versions', () => {
  306. assert(/^\d+\.\d+\.\d+\.\d+$/.test(process.versions.chrome))
  307. })
  308. })