Merge branch 'master' into native-window-open

This commit is contained in:
Ryohei Ikegami
2017-04-18 21:59:22 +09:00
141 changed files with 2393 additions and 748 deletions

View File

@@ -137,6 +137,16 @@ describe('app module', function () {
done()
})
})
it('closes all windows', function (done) {
var appPath = path.join(__dirname, 'fixtures', 'api', 'exit-closes-all-windows-app')
var electronPath = remote.getGlobal('process').execPath
appProcess = ChildProcess.spawn(electronPath, [appPath])
appProcess.on('close', function (code) {
assert.equal(code, 123)
done()
})
})
})
describe('app.relaunch', function () {

View File

@@ -0,0 +1,92 @@
'use strict'
const assert = require('assert')
const {closeWindow} = require('./window-helpers')
const {remote} = require('electron')
const {BrowserView, BrowserWindow} = remote
describe('View module', function () {
var w = null
var view = null
beforeEach(function () {
w = new BrowserWindow({
show: false,
width: 400,
height: 400,
webPreferences: {
backgroundThrottling: false
}
})
})
afterEach(function () {
if (view) {
view.destroy()
view = null
}
return closeWindow(w).then(function () { w = null })
})
describe('BrowserView.setBackgroundColor()', function () {
it('does not throw for valid args', function () {
view = new BrowserView()
view.setBackgroundColor('#000')
})
it('throws for invalid args', function () {
view = new BrowserView()
assert.throws(function () {
view.setBackgroundColor(null)
}, /conversion failure/)
})
})
describe('BrowserView.setAutoResize()', function () {
it('does not throw for valid args', function () {
view = new BrowserView()
view.setAutoResize({})
view.setAutoResize({ width: true, height: false })
})
it('throws for invalid args', function () {
view = new BrowserView()
assert.throws(function () {
view.setAutoResize(null)
}, /conversion failure/)
})
})
describe('BrowserView.setBounds()', function () {
it('does not throw for valid args', function () {
view = new BrowserView()
view.setBounds({ x: 0, y: 0, width: 1, height: 1 })
})
it('throws for invalid args', function () {
view = new BrowserView()
assert.throws(function () {
view.setBounds(null)
}, /conversion failure/)
assert.throws(function () {
view.setBounds({})
}, /conversion failure/)
})
})
describe('BrowserWindow.setBrowserView()', function () {
it('does not throw for valid args', function () {
view = new BrowserView()
w.setBrowserView(view)
})
it('does not throw if called multiple times with same view', function () {
view = new BrowserView()
w.setBrowserView(view)
w.setBrowserView(view)
w.setBrowserView(view)
})
})
})

View File

@@ -15,24 +15,32 @@ describe('crashReporter module', function () {
if (process.mas) {
return
}
var originalTempDirectory = null
var tempDirectory = null
before(function () {
tempDirectory = temp.mkdirSync('electronCrashReporterSpec-')
originalTempDirectory = app.getPath('temp')
app.setPath('temp', tempDirectory)
})
after(function () {
app.setPath('temp', originalTempDirectory)
})
var fixtures = path.resolve(__dirname, 'fixtures')
const generateSpecs = (description, browserWindowOpts) => {
describe(description, function () {
var w = null
var originalTempDirectory = null
var tempDirectory = null
beforeEach(function () {
w = new BrowserWindow(Object.assign({
show: false
}, browserWindowOpts))
tempDirectory = temp.mkdirSync('electronCrashReporterSpec-')
originalTempDirectory = app.getPath('temp')
app.setPath('temp', tempDirectory)
})
afterEach(function () {
app.setPath('temp', originalTempDirectory)
return closeWindow(w).then(function () { w = null })
})
@@ -77,13 +85,15 @@ describe('crashReporter module', function () {
it('should not send minidump if uploadToServer is false', function (done) {
this.timeout(120000)
if (process.platform === 'darwin') {
crashReporter.setUploadToServer(false)
}
let server
let dumpFile
let crashesDir
let crashesDir = crashReporter.getCrashesDirectory()
const existingDumpFiles = new Set()
if (process.platform === 'darwin') {
// crashpad puts the dump files in the "completed" subdirectory
crashesDir = path.join(crashesDir, 'completed')
crashReporter.setUploadToServer(false)
}
const testDone = (uploaded) => {
if (uploaded) {
return done(new Error('fail'))
@@ -93,7 +103,6 @@ describe('crashReporter module', function () {
crashReporter.setUploadToServer(true)
}
assert(fs.existsSync(dumpFile))
fs.unlinkSync(dumpFile)
done()
}
@@ -103,7 +112,7 @@ describe('crashReporter module', function () {
if (err) {
return
}
const dumps = files.filter((file) => /\.dmp$/.test(file))
const dumps = files.filter((file) => /\.dmp$/.test(file) && !existingDumpFiles.has(file))
if (!dumps.length) {
return
}
@@ -111,34 +120,17 @@ describe('crashReporter module', function () {
dumpFile = path.join(crashesDir, dumps[0])
clearInterval(pollInterval)
// dump file should not be deleted when not uploading, so we wait
// 500 ms and assert it still exists in `testDone`
setTimeout(testDone, 500)
// 1s and assert it still exists in `testDone`
setTimeout(testDone, 1000)
})
}
remote.ipcMain.once('set-crash-directory', (event, dir) => {
if (process.platform === 'linux') {
crashesDir = dir
} else {
crashesDir = crashReporter.getCrashesDirectory()
if (process.platform === 'darwin') {
// crashpad uses an extra subdirectory
crashesDir = path.join(crashesDir, 'completed')
}
}
// Before starting, remove all dump files in the crash directory.
// This is required because:
// - mac crashpad not seem to allow changing the crash directory after
// the first "start" call.
// - Other tests in this suite may leave dumps there.
// - We want to verify in `testDone` that a dump file is created and
// not deleted.
remote.ipcMain.once('list-existing-dumps', (event) => {
fs.readdir(crashesDir, (err, files) => {
if (!err) {
for (const file of files) {
if (/\.dmp$/.test(file)) {
fs.unlinkSync(path.join(crashesDir, file))
existingDumpFiles.add(file)
}
}
}

View File

@@ -93,4 +93,20 @@ describe('dialog module', () => {
}, /Error processing argument at index 1/)
})
})
describe('showCertificateTrustDialog', () => {
it('throws errors when the options are invalid', () => {
assert.throws(() => {
dialog.showCertificateTrustDialog()
}, /options must be an object/)
assert.throws(() => {
dialog.showCertificateTrustDialog({})
}, /certificate must be an object/)
assert.throws(() => {
dialog.showCertificateTrustDialog({certificate: {}, message: false})
}, /message must be a string/)
})
})
})

View File

@@ -175,8 +175,14 @@ describe('ipc module', function () {
it('can change its properties', function () {
var property = remote.require(path.join(fixtures, 'module', 'property.js'))
assert.equal(property.property, 1127)
property.property = null
assert.equal(property.property, null)
property.property = undefined
assert.equal(property.property, undefined)
property.property = 1007
assert.equal(property.property, 1007)
assert.equal(property.getFunctionProperty(), 'foo-browser')
property.func.property = 'bar'
assert.equal(property.getFunctionProperty(), 'bar-browser')
@@ -199,6 +205,14 @@ describe('ipc module', function () {
}, /setting error/)
})
it('can set a remote property with a remote object', function () {
const foo = remote.require(path.join(fixtures, 'module', 'remote-object-set.js'))
assert.doesNotThrow(function () {
foo.bar = remote.getCurrentWindow()
})
})
it('can construct an object from its member', function () {
var call = remote.require(path.join(fixtures, 'module', 'call.js'))
var obj = new call.constructor()

View File

@@ -26,22 +26,34 @@ const kOneKiloByte = 1024
const kOneMegaByte = kOneKiloByte * kOneKiloByte
describe('net module', function () {
describe('HTTP basics', function () {
let server
beforeEach(function (done) {
server = http.createServer()
server.listen(0, '127.0.0.1', function () {
server.url = 'http://127.0.0.1:' + server.address().port
done()
let server
const connections = new Set()
beforeEach(function (done) {
server = http.createServer()
server.listen(0, '127.0.0.1', function () {
server.url = `http://127.0.0.1:${server.address().port}`
done()
})
server.on('connection', (connection) => {
connections.add(connection)
connection.once('close', () => {
connections.delete(connection)
})
})
})
afterEach(function () {
server.close(function () {
})
afterEach(function (done) {
for (const connection of connections) {
connection.destroy()
}
server.close(function () {
server = null
done()
})
})
describe('HTTP basics', function () {
it('should be able to issue a basic GET request', function (done) {
const requestUrl = '/requestUrl'
server.on('request', function (request, response) {
@@ -224,19 +236,7 @@ describe('net module', function () {
})
describe('ClientRequest API', function () {
let server
beforeEach(function (done) {
server = http.createServer()
server.listen(0, '127.0.0.1', function () {
server.url = 'http://127.0.0.1:' + server.address().port
done()
})
})
afterEach(function () {
server.close(function () {
})
server = null
session.defaultSession.webRequest.onBeforeRequest(null)
})
@@ -1363,21 +1363,8 @@ describe('net module', function () {
urlRequest.end()
})
})
describe('IncomingMessage API', function () {
let server
beforeEach(function (done) {
server = http.createServer()
server.listen(0, '127.0.0.1', function () {
server.url = 'http://127.0.0.1:' + server.address().port
done()
})
})
afterEach(function () {
server.close()
server = null
})
it('response object should implement the IncomingMessage API', function (done) {
const requestUrl = '/requestUrl'
const customHeaderName = 'Some-Custom-Header-Name'
@@ -1544,21 +1531,8 @@ describe('net module', function () {
urlRequest.end()
})
})
describe('Stability and performance', function (done) {
let server
beforeEach(function (done) {
server = http.createServer()
server.listen(0, '127.0.0.1', function () {
server.url = 'http://127.0.0.1:' + server.address().port
done()
})
})
afterEach(function () {
server.close()
server = null
})
it('should free unreferenced, never-started request objects without crash', function (done) {
const requestUrl = '/requestUrl'
ipcRenderer.once('api-net-spec-done', function () {
@@ -1574,6 +1548,7 @@ describe('net module', function () {
})
`)
})
it('should not collect on-going requests without crash', function (done) {
const requestUrl = '/requestUrl'
server.on('request', function (request, response) {
@@ -1615,6 +1590,7 @@ describe('net module', function () {
urlRequest.end()
`)
})
it('should collect unreferenced, ended requests without crash', function (done) {
const requestUrl = '/requestUrl'
server.on('request', function (request, response) {

View File

@@ -6,20 +6,32 @@ const {TouchBarButton, TouchBarColorPicker, TouchBarGroup} = TouchBar
const {TouchBarLabel, TouchBarPopover, TouchBarScrubber, TouchBarSegmentedControl, TouchBarSlider, TouchBarSpacer} = TouchBar
describe('TouchBar module', function () {
it('throws an error when created without an items array', function () {
it('throws an error when created without an options object', function () {
assert.throws(() => {
const touchBar = new TouchBar()
touchBar.toString()
}, /Must specify items array as first argument/)
}, /Must specify options object as first argument/)
})
it('throws an error when created with invalid items', function () {
assert.throws(() => {
const touchBar = new TouchBar([1, true, {}, []])
const touchBar = new TouchBar({items: [1, true, {}, []]})
touchBar.toString()
}, /Each item must be an instance of TouchBarItem/)
})
it('throws an error when an invalid escape item is set', function () {
assert.throws(() => {
const touchBar = new TouchBar({items: [], escapeItem: 'esc'})
touchBar.toString()
}, /Escape item must be an instance of TouchBarItem/)
assert.throws(() => {
const touchBar = new TouchBar({items: []})
touchBar.escapeItem = 'esc'
}, /Escape item must be an instance of TouchBarItem/)
})
describe('BrowserWindow behavior', function () {
let window
@@ -55,10 +67,40 @@ describe('TouchBar module', function () {
showArrowButtons: true
})
])
const escapeButton = new TouchBarButton({
label: 'foo'
})
window.setTouchBar(touchBar)
touchBar.escapeItem = escapeButton
label.label = 'baz'
escapeButton.label = 'hello'
window.setTouchBar()
window.setTouchBar(new TouchBar([new TouchBarLabel({label: 'two'})]))
touchBar.escapeItem = null
})
it('calls the callback on the items when a window interaction event fires', function (done) {
const button = new TouchBarButton({
label: 'bar',
click: () => {
done()
}
})
const touchBar = new TouchBar({items: [button]})
window.setTouchBar(touchBar)
window.emit('-touch-bar-interaction', {}, button.id)
})
it('calls the callback on the escape item when a window interaction event fires', function (done) {
const button = new TouchBarButton({
label: 'bar',
click: () => {
done()
}
})
const touchBar = new TouchBar({escapeItem: button})
window.setTouchBar(touchBar)
window.emit('-touch-bar-interaction', {}, button.id)
})
})
})

View File

@@ -17,7 +17,7 @@ crashReporter.start({
}
});
if (!uploadToServer) {
ipcRenderer.sendSync('set-crash-directory', crashReporter.getCrashesDirectory())
ipcRenderer.sendSync('list-existing-dumps')
}
setImmediate(function() { process.crash(); });
</script>

View File

@@ -0,0 +1,19 @@
const {app, BrowserWindow} = require('electron')
const windows = []
function createWindow (id) {
const window = new BrowserWindow({show: false})
window.loadURL(`data:,window${id}`)
windows.push(window)
}
app.once('ready', () => {
for (let i = 1; i <= 5; i++) {
createWindow(i)
}
setImmediate(function () {
app.exit(123)
})
})

View File

@@ -0,0 +1,4 @@
{
"name": "electron-exit-closes-all-windows",
"main": "main.js"
}

View File

@@ -0,0 +1,11 @@
const {BrowserWindow} = require('electron')
class Foo {
set bar (value) {
if (!(value instanceof BrowserWindow)) {
throw new Error('setting error')
}
}
}
module.exports = new Foo()

View File

@@ -91,6 +91,16 @@ describe('node feature', function () {
done()
})
})
it('works when sending a message to a process forked with the --eval argument', function (done) {
const source = "process.on('message', (message) => { process.send(message) })"
const forked = ChildProcess.fork('--eval', [source])
forked.once('message', (message) => {
assert.equal(message, 'hello')
done()
})
forked.send('hello')
})
})
describe('child_process.spawn', function () {