electron/spec/chromium-spec.js
Cheng Zhao cc6bcb6c81 Fix some flaky tests in CI (#12153)
* Guard whole InitPrefs with ScopedAllowIO

Saw a crash:
0 0x7f8d2f7d918d base::debug::StackTrace::StackTrace()
1 0x7f8d2f7d755c base::debug::StackTrace::StackTrace()
2 0x7f8d2f867caa logging::LogMessage::~LogMessage()
3 0x7f8d2fa157c7 base::ThreadRestrictions::AssertIOAllowed()
4 0x7f8d2f83453a base::OpenFile()
5 0x7f8d2f82a967 base::ReadFileToStringWithMaxSize()
6 0x7f8d2f82ad44 base::ReadFileToString()
7 0x7f8d2f846f73 JSONFileValueDeserializer::ReadFileToString()
8 0x7f8d2f84738c JSONFileValueDeserializer::Deserialize()
9 0x7f8d35a5d1f6 <unknown>
10 0x7f8d35a5c217 JsonPrefStore::ReadPrefs()
11 0x7f8d35a87d3e PrefService::InitFromStorage()
12 0x7f8d35a87c60 PrefService::PrefService()
13 0x7f8d35a91a10 PrefServiceFactory::Create()
14 0x000000e86e1b brightray::BrowserContext::InitPrefs()
15 0x000000c2bd64 atom::AtomBrowserContext::AtomBrowserContext()
16 0x000000c320db atom::AtomBrowserContext::From()
17 0x000000b4b8b5 atom::api::Session::FromPartition()

* Fix done being called twice in setInterval test

The callback passed to browser process is called asyncly, so it is
possible that multiple callbacks has already been scheduled before we
can clearInternval.

* Fix failing test when dir name has special chars

The pdfSource is not escaped while parsedURL.search is.

* Call done with Error instead of string

* Fix crash caused by not removing input observer

Solve crash:
0 libcontent.dylib content::RenderWidgetHostImpl::DispatchInputEventWithLatencyInfo(blink::WebInputEvent const&, ui::LatencyInfo*) + 214
1 libcontent.dylib content::RenderWidgetHostImpl::ForwardMouseEventWithLatencyInfo(blink::WebMouseEvent const&, ui::LatencyInfo const&) + 1350
2 libcontent.dylib content::RenderWidgetHostViewMac::ProcessMouseEvent(blink::WebMouseEvent const&, ui::LatencyInfo const&) + 44
3 libcontent.dylib content::RenderWidgetHostInputEventRouter::RouteMouseEvent(content::RenderWidgetHostViewBase*, blink::WebMouseEvent*, ui::LatencyInfo const&) + 1817

* Print detailed error

* Run tests after server is ready
2018-03-07 14:40:27 +09:00

1128 lines
35 KiB
JavaScript

const assert = require('assert')
const fs = require('fs')
const http = require('http')
const path = require('path')
const ws = require('ws')
const url = require('url')
const ChildProcess = require('child_process')
const {ipcRenderer, remote} = require('electron')
const {closeWindow} = require('./window-helpers')
const {app, BrowserWindow, ipcMain, protocol, session, webContents} = remote
const isCI = remote.getGlobal('isCi')
/* Most of the APIs here don't use standard callbacks */
/* eslint-disable standard/no-callback-literal */
describe('chromium feature', () => {
const fixtures = path.resolve(__dirname, 'fixtures')
let listener = null
let w = null
afterEach(() => {
if (listener != null) {
window.removeEventListener('message', listener)
}
listener = null
})
describe('command line switches', () => {
describe('--lang switch', () => {
const testLocale = (locale, result, done) => {
const appPath = path.join(__dirname, 'fixtures', 'api', 'locale-check')
const electronPath = remote.getGlobal('process').execPath
let output = ''
let appProcess = ChildProcess.spawn(electronPath, [appPath, `--lang=${locale}`])
appProcess.stdout.on('data', (data) => { output += data })
appProcess.stdout.on('end', () => {
output = output.replace(/(\r\n|\n|\r)/gm, '')
assert.equal(output, result)
done()
})
}
it('should set the locale', (done) => testLocale('fr', 'fr', done))
it('should not set an invalid locale', (done) => testLocale('asdfkl', 'en-US', done))
})
})
afterEach(() => closeWindow(w).then(() => { w = null }))
describe('heap snapshot', () => {
it('does not crash', function () {
process.atomBinding('v8_util').takeHeapSnapshot()
})
})
describe('sending request of http protocol urls', () => {
it('does not crash', (done) => {
const server = http.createServer((req, res) => {
res.end()
server.close()
done()
})
server.listen(0, '127.0.0.1', () => {
const port = server.address().port
$.get(`http://127.0.0.1:${port}`)
})
})
})
describe('navigator.webkitGetUserMedia', () => {
it('calls its callbacks', (done) => {
navigator.webkitGetUserMedia({
audio: true,
video: false
}, () => done(),
() => done())
})
})
describe('navigator.mediaDevices', () => {
if (isCI) return
it('can return labels of enumerated devices', (done) => {
navigator.mediaDevices.enumerateDevices().then((devices) => {
const labels = devices.map((device) => device.label)
const labelFound = labels.some((label) => !!label)
if (labelFound) {
done()
} else {
done(new Error(`No device labels found: ${JSON.stringify(labels)}`))
}
}).catch(done)
})
it('can return new device id when cookie storage is cleared', (done) => {
const options = {
origin: null,
storages: ['cookies']
}
const deviceIds = []
const ses = session.fromPartition('persist:media-device-id')
w = new BrowserWindow({
show: false,
webPreferences: {
session: ses
}
})
w.webContents.on('ipc-message', (event, args) => {
if (args[0] === 'deviceIds') deviceIds.push(args[1])
if (deviceIds.length === 2) {
assert.notDeepEqual(deviceIds[0], deviceIds[1])
closeWindow(w).then(() => {
w = null
done()
}).catch((error) => done(error))
} else {
ses.clearStorageData(options, () => {
w.webContents.reload()
})
}
})
w.loadURL(`file://${fixtures}/pages/media-id-reset.html`)
})
})
describe('navigator.language', () => {
it('should not be empty', () => {
assert.notEqual(navigator.language, '')
})
})
describe('navigator.serviceWorker', () => {
it('should register for file scheme', (done) => {
w = new BrowserWindow({ show: false })
w.webContents.on('ipc-message', (event, args) => {
if (args[0] === 'reload') {
w.webContents.reload()
} else if (args[0] === 'error') {
done(new Error(`unexpected error : ${JSON.stringify(args[1])}`))
} else if (args[0] === 'response') {
assert.equal(args[1], 'Hello from serviceWorker!')
session.defaultSession.clearStorageData({
storages: ['serviceworkers']
}, () => done())
}
})
w.webContents.on('crashed', () => done(new Error('WebContents crashed.')))
w.loadURL(`file://${fixtures}/pages/service-worker/index.html`)
})
it('should register for intercepted file scheme', (done) => {
const customSession = session.fromPartition('intercept-file')
customSession.protocol.interceptBufferProtocol('file', (request, callback) => {
let file = url.parse(request.url).pathname
if (file[0] === '/' && process.platform === 'win32') file = file.slice(1)
const content = fs.readFileSync(path.normalize(file))
const ext = path.extname(file)
let type = 'text/html'
if (ext === '.js') type = 'application/javascript'
callback({data: content, mimeType: type})
}, (error) => {
if (error) done(error)
})
w = new BrowserWindow({
show: false,
webPreferences: { session: customSession }
})
w.webContents.on('ipc-message', (event, args) => {
if (args[0] === 'reload') {
w.webContents.reload()
} else if (args[0] === 'error') {
done(`unexpected error : ${args[1]}`)
} else if (args[0] === 'response') {
assert.equal(args[1], 'Hello from serviceWorker!')
customSession.clearStorageData({
storages: ['serviceworkers']
}, () => {
customSession.protocol.uninterceptProtocol('file', (error) => done(error))
})
}
})
w.webContents.on('crashed', () => done(new Error('WebContents crashed.')))
w.loadURL(`file://${fixtures}/pages/service-worker/index.html`)
})
})
describe('window.open', () => {
it('returns a BrowserWindowProxy object', () => {
const b = window.open('about:blank', '', 'show=no')
assert.equal(b.closed, false)
assert.equal(b.constructor.name, 'BrowserWindowProxy')
b.close()
})
it('accepts "nodeIntegration" as feature', (done) => {
let b
listener = (event) => {
assert.equal(event.data.isProcessGlobalUndefined, true)
b.close()
done()
}
window.addEventListener('message', listener)
b = window.open(`file://${fixtures}/pages/window-opener-node.html`, '', 'nodeIntegration=no,show=no')
})
it('inherit options of parent window', (done) => {
let b
listener = (event) => {
const ref1 = remote.getCurrentWindow().getSize()
const width = ref1[0]
const height = ref1[1]
assert.equal(event.data, `size: ${width} ${height}`)
b.close()
done()
}
window.addEventListener('message', listener)
b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no')
})
it('disables node integration when it is disabled on the parent window', (done) => {
let b
listener = (event) => {
assert.equal(event.data.isProcessGlobalUndefined, true)
b.close()
done()
}
window.addEventListener('message', listener)
const windowUrl = require('url').format({
pathname: `${fixtures}/pages/window-opener-no-node-integration.html`,
protocol: 'file',
query: {
p: `${fixtures}/pages/window-opener-node.html`
},
slashes: true
})
b = window.open(windowUrl, '', 'nodeIntegration=no,show=no')
})
it('disables node integration when it is disabled on the parent window for chrome devtools URLs', (done) => {
let b
app.once('web-contents-created', (event, contents) => {
contents.once('did-finish-load', () => {
contents.executeJavaScript('typeof process').then((typeofProcessGlobal) => {
assert.equal(typeofProcessGlobal, 'undefined')
b.close()
done()
}).catch(done)
})
})
b = window.open('chrome-devtools://devtools/bundled/inspector.html', '', 'nodeIntegration=no,show=no')
})
it('disables JavaScript when it is disabled on the parent window', (done) => {
let b
app.once('web-contents-created', (event, contents) => {
contents.once('did-finish-load', () => {
app.once('browser-window-created', (event, window) => {
const preferences = window.webContents.getWebPreferences()
assert.equal(preferences.javascript, false)
window.destroy()
b.close()
done()
})
// Click link on page
contents.sendInputEvent({type: 'mouseDown', clickCount: 1, x: 1, y: 1})
contents.sendInputEvent({type: 'mouseUp', clickCount: 1, x: 1, y: 1})
})
})
const windowUrl = require('url').format({
pathname: `${fixtures}/pages/window-no-javascript.html`,
protocol: 'file',
slashes: true
})
b = window.open(windowUrl, '', 'javascript=no,show=no')
})
it('disables the <webview> tag when it is disabled on the parent window', (done) => {
let b
listener = (event) => {
assert.equal(event.data.isWebViewGlobalUndefined, true)
b.close()
done()
}
window.addEventListener('message', listener)
const windowUrl = require('url').format({
pathname: `${fixtures}/pages/window-opener-no-webview-tag.html`,
protocol: 'file',
query: {
p: `${fixtures}/pages/window-opener-webview.html`
},
slashes: true
})
b = window.open(windowUrl, '', 'webviewTag=no,nodeIntegration=yes,show=no')
})
it('does not override child options', (done) => {
let b
const size = {
width: 350,
height: 450
}
listener = (event) => {
assert.equal(event.data, `size: ${size.width} ${size.height}`)
b.close()
done()
}
window.addEventListener('message', listener)
b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no,width=' + size.width + ',height=' + size.height)
})
it('handles cycles when merging the parent options into the child options', (done) => {
w = BrowserWindow.fromId(ipcRenderer.sendSync('create-window-with-options-cycle'))
w.loadURL(`file://${fixtures}/pages/window-open.html`)
w.webContents.once('new-window', (event, url, frameName, disposition, options) => {
assert.equal(options.show, false)
assert.deepEqual(options.foo, {
bar: null,
baz: {
hello: {
world: true
}
},
baz2: {
hello: {
world: true
}
}
})
done()
})
})
it('defines a window.location getter', (done) => {
let b
let targetURL
if (process.platform === 'win32') {
targetURL = `file:///${fixtures.replace(/\\/g, '/')}/pages/base-page.html`
} else {
targetURL = `file://${fixtures}/pages/base-page.html`
}
app.once('browser-window-created', (event, window) => {
window.webContents.once('did-finish-load', () => {
assert.equal(b.location, targetURL)
b.close()
done()
})
})
b = window.open(targetURL)
})
it('defines a window.location setter', (done) => {
let b
app.once('browser-window-created', (event, {webContents}) => {
webContents.once('did-finish-load', () => {
// When it loads, redirect
b.location = `file://${fixtures}/pages/base-page.html`
webContents.once('did-finish-load', () => {
// After our second redirect, cleanup and callback
b.close()
done()
})
})
})
b = window.open('about:blank')
})
it('open a blank page when no URL is specified', (done) => {
let b
app.once('browser-window-created', (event, {webContents}) => {
webContents.once('did-finish-load', () => {
const {location} = b
b.close()
assert.equal(location, 'about:blank')
let c
app.once('browser-window-created', (event, {webContents}) => {
webContents.once('did-finish-load', () => {
const {location} = c
c.close()
assert.equal(location, 'about:blank')
done()
})
})
c = window.open('')
})
})
b = window.open()
})
it('throws an exception when the arguments cannot be converted to strings', () => {
assert.throws(() => {
window.open('', {toString: null})
}, /Cannot convert object to primitive value/)
assert.throws(() => {
window.open('', '', {toString: 3})
}, /Cannot convert object to primitive value/)
})
it('sets the window title to the specified frameName', (done) => {
let b
app.once('browser-window-created', (event, createdWindow) => {
assert.equal(createdWindow.getTitle(), 'hello')
b.close()
done()
})
b = window.open('', 'hello')
})
it('does not throw an exception when the frameName is a built-in object property', (done) => {
let b
app.once('browser-window-created', (event, createdWindow) => {
assert.equal(createdWindow.getTitle(), '__proto__')
b.close()
done()
})
b = window.open('', '__proto__')
})
it('does not throw an exception when the features include webPreferences', () => {
let b
assert.doesNotThrow(() => {
b = window.open('', '', 'webPreferences=')
})
b.close()
})
})
describe('window.opener', () => {
let url = `file://${fixtures}/pages/window-opener.html`
it('is null for main window', (done) => {
w = new BrowserWindow({ show: false })
w.webContents.once('ipc-message', (event, args) => {
assert.deepEqual(args, ['opener', null])
done()
})
w.loadURL(url)
})
it('is not null for window opened by window.open', (done) => {
let b
listener = (event) => {
assert.equal(event.data, 'object')
b.close()
done()
}
window.addEventListener('message', listener)
b = window.open(url, '', 'show=no')
})
})
describe('window.opener access from BrowserWindow', () => {
const scheme = 'other'
let url = `${scheme}://${fixtures}/pages/window-opener-location.html`
let w = null
before((done) => {
protocol.registerFileProtocol(scheme, (request, callback) => {
callback(`${fixtures}/pages/window-opener-location.html`)
}, (error) => done(error))
})
after(() => {
protocol.unregisterProtocol(scheme)
})
afterEach(() => {
w.close()
})
it('does nothing when origin of current window does not match opener', (done) => {
listener = (event) => {
assert.equal(event.data, undefined)
done()
}
window.addEventListener('message', listener)
w = window.open(url, '', 'show=no')
})
it('works when origin matches', (done) => {
listener = (event) => {
assert.equal(event.data, location.href)
done()
}
window.addEventListener('message', listener)
w = window.open(`file://${fixtures}/pages/window-opener-location.html`, '', 'show=no')
})
it('works when origin does not match opener but has node integration', (done) => {
listener = (event) => {
assert.equal(event.data, location.href)
done()
}
window.addEventListener('message', listener)
w = window.open(url, '', 'show=no,nodeIntegration=yes')
})
})
describe('window.opener access from <webview>', () => {
const scheme = 'other'
const srcPath = `${fixtures}/pages/webview-opener-postMessage.html`
const pageURL = `file://${fixtures}/pages/window-opener-location.html`
let webview = null
before((done) => {
protocol.registerFileProtocol(scheme, (request, callback) => {
callback(srcPath)
}, (error) => done(error))
})
after(() => {
protocol.unregisterProtocol(scheme)
})
afterEach(() => {
if (webview != null) webview.remove()
})
it('does nothing when origin of webview src URL does not match opener', (done) => {
webview = new WebView()
webview.addEventListener('console-message', (e) => {
assert.equal(e.message, 'null')
done()
})
webview.setAttribute('allowpopups', 'on')
webview.src = url.format({
pathname: srcPath,
protocol: scheme,
query: {
p: pageURL
},
slashes: true
})
document.body.appendChild(webview)
})
it('works when origin matches', (done) => {
webview = new WebView()
webview.addEventListener('console-message', (e) => {
assert.equal(e.message, webview.src)
done()
})
webview.setAttribute('allowpopups', 'on')
webview.src = url.format({
pathname: srcPath,
protocol: 'file',
query: {
p: pageURL
},
slashes: true
})
document.body.appendChild(webview)
})
it('works when origin does not match opener but has node integration', (done) => {
webview = new WebView()
webview.addEventListener('console-message', (e) => {
webview.remove()
assert.equal(e.message, webview.src)
done()
})
webview.setAttribute('allowpopups', 'on')
webview.setAttribute('nodeintegration', 'on')
webview.src = url.format({
pathname: srcPath,
protocol: scheme,
query: {
p: pageURL
},
slashes: true
})
document.body.appendChild(webview)
})
})
describe('window.postMessage', () => {
it('sets the source and origin correctly', (done) => {
let b
listener = (event) => {
window.removeEventListener('message', listener)
b.close()
const message = JSON.parse(event.data)
assert.equal(message.data, 'testing')
assert.equal(message.origin, 'file://')
assert.equal(message.sourceEqualsOpener, true)
assert.equal(event.origin, 'file://')
done()
}
window.addEventListener('message', listener)
app.once('browser-window-created', (event, {webContents}) => {
webContents.once('did-finish-load', () => {
b.postMessage('testing', '*')
})
})
b = window.open(`file://${fixtures}/pages/window-open-postMessage.html`, '', 'show=no')
})
it('throws an exception when the targetOrigin cannot be converted to a string', () => {
const b = window.open('')
assert.throws(() => {
b.postMessage('test', {toString: null})
}, /Cannot convert object to primitive value/)
b.close()
})
})
describe('window.opener.postMessage', () => {
it('sets source and origin correctly', (done) => {
let b
listener = (event) => {
window.removeEventListener('message', listener)
b.close()
assert.equal(event.source, b)
assert.equal(event.origin, 'file://')
done()
}
window.addEventListener('message', listener)
b = window.open(`file://${fixtures}/pages/window-opener-postMessage.html`, '', 'show=no')
})
it('supports windows opened from a <webview>', (done) => {
const webview = new WebView()
webview.addEventListener('console-message', (e) => {
webview.remove()
assert.equal(e.message, 'message')
done()
})
webview.allowpopups = true
webview.src = url.format({
pathname: `${fixtures}/pages/webview-opener-postMessage.html`,
protocol: 'file',
query: {
p: `${fixtures}/pages/window-opener-postMessage.html`
},
slashes: true
})
document.body.appendChild(webview)
})
describe('targetOrigin argument', () => {
let serverURL
let server
beforeEach((done) => {
server = http.createServer((req, res) => {
res.writeHead(200)
const filePath = path.join(fixtures, 'pages', 'window-opener-targetOrigin.html')
res.end(fs.readFileSync(filePath, 'utf8'))
})
server.listen(0, '127.0.0.1', () => {
serverURL = `http://127.0.0.1:${server.address().port}`
done()
})
})
afterEach(() => {
server.close()
})
it('delivers messages that match the origin', (done) => {
let b
listener = (event) => {
window.removeEventListener('message', listener)
b.close()
assert.equal(event.data, 'deliver')
done()
}
window.addEventListener('message', listener)
b = window.open(serverURL, '', 'show=no')
})
})
})
describe('creating a Uint8Array under browser side', () => {
it('does not crash', () => {
const RUint8Array = remote.getGlobal('Uint8Array')
const arr = new RUint8Array()
assert(arr)
})
})
describe('webgl', () => {
before(function () {
if (isCI && process.platform === 'win32') {
this.skip()
}
})
it('can be get as context in canvas', () => {
if (process.platform === 'linux') {
// FIXME(alexeykuzmin): Skip the test.
// this.skip()
return
}
const webgl = document.createElement('canvas').getContext('webgl')
assert.notEqual(webgl, null)
})
})
describe('web workers', () => {
it('Worker can work', (done) => {
const worker = new Worker('../fixtures/workers/worker.js')
const message = 'ping'
worker.onmessage = (event) => {
assert.equal(event.data, message)
worker.terminate()
done()
}
worker.postMessage(message)
})
it('Worker has no node integration by default', (done) => {
let worker = new Worker('../fixtures/workers/worker_node.js')
worker.onmessage = (event) => {
assert.equal(event.data, 'undefined undefined undefined undefined')
worker.terminate()
done()
}
})
it('Worker has node integration with nodeIntegrationInWorker', (done) => {
let webview = new WebView()
webview.addEventListener('ipc-message', (e) => {
assert.equal(e.channel, 'object function object function')
webview.remove()
done()
})
webview.src = `file://${fixtures}/pages/worker.html`
webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker')
document.body.appendChild(webview)
})
it('SharedWorker can work', (done) => {
const worker = new SharedWorker('../fixtures/workers/shared_worker.js')
const message = 'ping'
worker.port.onmessage = (event) => {
assert.equal(event.data, message)
done()
}
worker.port.postMessage(message)
})
it('SharedWorker has no node integration by default', (done) => {
let worker = new SharedWorker('../fixtures/workers/shared_worker_node.js')
worker.port.onmessage = (event) => {
assert.equal(event.data, 'undefined undefined undefined undefined')
done()
}
})
it('SharedWorker has node integration with nodeIntegrationInWorker', (done) => {
let webview = new WebView()
webview.addEventListener('console-message', (e) => {
console.log(e)
})
webview.addEventListener('ipc-message', (e) => {
assert.equal(e.channel, 'object function object function')
webview.remove()
done()
})
webview.src = `file://${fixtures}/pages/shared_worker.html`
webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker')
document.body.appendChild(webview)
})
})
describe('iframe', () => {
let iframe = null
beforeEach(() => {
iframe = document.createElement('iframe')
})
afterEach(() => {
document.body.removeChild(iframe)
})
it('does not have node integration', (done) => {
iframe.src = `file://${fixtures}/pages/set-global.html`
document.body.appendChild(iframe)
iframe.onload = () => {
assert.equal(iframe.contentWindow.test, 'undefined undefined undefined')
done()
}
})
})
describe('storage', () => {
it('requesting persitent quota works', (done) => {
navigator.webkitPersistentStorage.requestQuota(1024 * 1024, (grantedBytes) => {
assert.equal(grantedBytes, 1048576)
done()
})
})
describe('custom non standard schemes', () => {
const protocolName = 'storage'
let contents = null
before((done) => {
const handler = (request, callback) => {
let parsedUrl = url.parse(request.url)
let filename
switch (parsedUrl.pathname) {
case '/localStorage' : filename = 'local_storage.html'; break
case '/sessionStorage' : filename = 'session_storage.html'; break
case '/WebSQL' : filename = 'web_sql.html'; break
case '/indexedDB' : filename = 'indexed_db.html'; break
case '/cookie' : filename = 'cookie.html'; break
default : filename = ''
}
callback({path: `${fixtures}/pages/storage/${filename}`})
}
protocol.registerFileProtocol(protocolName, handler, (error) => done(error))
})
after((done) => {
protocol.unregisterProtocol(protocolName, () => done())
})
beforeEach(() => {
contents = webContents.create({})
})
afterEach(() => {
contents.destroy()
contents = null
})
it('cannot access localStorage', (done) => {
ipcMain.once('local-storage-response', (event, error) => {
assert.equal(
error,
'Failed to read the \'localStorage\' property from \'Window\': Access is denied for this document.')
done()
})
contents.loadURL(protocolName + '://host/localStorage')
})
it('cannot access sessionStorage', (done) => {
ipcMain.once('session-storage-response', (event, error) => {
assert.equal(
error,
'Failed to read the \'sessionStorage\' property from \'Window\': Access is denied for this document.')
done()
})
contents.loadURL(`${protocolName}://host/sessionStorage`)
})
it('cannot access WebSQL database', (done) => {
ipcMain.once('web-sql-response', (event, error) => {
assert.equal(
error,
'An attempt was made to break through the security policy of the user agent.')
done()
})
contents.loadURL(`${protocolName}://host/WebSQL`)
})
it('cannot access indexedDB', (done) => {
ipcMain.once('indexed-db-response', (event, error) => {
assert.equal(error, 'The user denied permission to access the database.')
done()
})
contents.loadURL(`${protocolName}://host/indexedDB`)
})
it('cannot access cookie', (done) => {
ipcMain.once('cookie-response', (event, cookie) => {
assert(!cookie)
done()
})
contents.loadURL(`${protocolName}://host/cookie`)
})
})
})
describe('websockets', () => {
let wss = null
let server = null
const WebSocketServer = ws.Server
afterEach(() => {
wss.close()
server.close()
})
it('has user agent', (done) => {
server = http.createServer()
server.listen(0, '127.0.0.1', () => {
const port = server.address().port
wss = new WebSocketServer({ server: server })
wss.on('error', done)
wss.on('connection', (ws) => {
if (ws.upgradeReq.headers['user-agent']) {
done()
} else {
done('user agent is empty')
}
})
const socket = new WebSocket(`ws://127.0.0.1:${port}`)
assert(socket)
})
})
})
describe('Promise', () => {
it('resolves correctly in Node.js calls', (done) => {
document.registerElement('x-element', {
prototype: Object.create(HTMLElement.prototype, {
createdCallback: {
value: () => {}
}
})
})
setImmediate(() => {
let called = false
Promise.resolve().then(() => {
done(called ? void 0 : new Error('wrong sequence'))
})
document.createElement('x-element')
called = true
})
})
it('resolves correctly in Electron calls', (done) => {
document.registerElement('y-element', {
prototype: Object.create(HTMLElement.prototype, {
createdCallback: {
value: () => {}
}
})
})
remote.getGlobal('setImmediate')(() => {
let called = false
Promise.resolve().then(() => {
done(called ? void 0 : new Error('wrong sequence'))
})
document.createElement('y-element')
called = true
})
})
})
describe('fetch', () => {
it('does not crash', (done) => {
const server = http.createServer((req, res) => {
res.end('test')
server.close()
})
server.listen(0, '127.0.0.1', () => {
const port = server.address().port
fetch(`http://127.0.0.1:${port}`).then((res) => res.body.getReader())
.then((reader) => {
reader.read().then((r) => {
reader.cancel()
done()
})
}).catch((e) => done(e))
})
})
})
describe('PDF Viewer', () => {
const pdfSource = url.format({
pathname: path.join(fixtures, 'assets', 'cat.pdf').replace(/\\/g, '/'),
protocol: 'file',
slashes: true
})
const pdfSourceWithParams = url.format({
pathname: path.join(fixtures, 'assets', 'cat.pdf').replace(/\\/g, '/'),
query: {
a: 1,
b: 2
},
protocol: 'file',
slashes: true
})
function createBrowserWindow ({plugins, preload}) {
w = new BrowserWindow({
show: false,
webPreferences: {
preload: path.join(fixtures, 'module', preload),
plugins: plugins
}
})
}
function testPDFIsLoadedInSubFrame (page, preloadFile, done) {
const pagePath = url.format({
pathname: path.join(fixtures, 'pages', page).replace(/\\/g, '/'),
protocol: 'file',
slashes: true
})
createBrowserWindow({plugins: true, preload: preloadFile})
ipcMain.once('pdf-loaded', (event, state) => {
assert.equal(state, 'success')
done()
})
w.webContents.on('page-title-updated', () => {
const parsedURL = url.parse(w.webContents.getURL(), true)
assert.equal(parsedURL.protocol, 'chrome:')
assert.equal(parsedURL.hostname, 'pdf-viewer')
assert.equal(parsedURL.query.src, pagePath)
assert.equal(w.webContents.getTitle(), 'cat.pdf')
})
w.webContents.loadURL(pagePath)
}
it('opens when loading a pdf resource as top level navigation', (done) => {
createBrowserWindow({plugins: true, preload: 'preload-pdf-loaded.js'})
ipcMain.once('pdf-loaded', (event, state) => {
assert.equal(state, 'success')
done()
})
w.webContents.on('page-title-updated', () => {
const parsedURL = url.parse(w.webContents.getURL(), true)
assert.equal(parsedURL.protocol, 'chrome:')
assert.equal(parsedURL.hostname, 'pdf-viewer')
assert.equal(parsedURL.query.src, pdfSource)
assert.equal(w.webContents.getTitle(), 'cat.pdf')
})
w.webContents.loadURL(pdfSource)
})
it('opens a pdf link given params, the query string should be escaped', (done) => {
createBrowserWindow({plugins: true, preload: 'preload-pdf-loaded.js'})
ipcMain.once('pdf-loaded', (event, state) => {
assert.equal(state, 'success')
done()
})
w.webContents.on('page-title-updated', () => {
const parsedURL = url.parse(w.webContents.getURL(), true)
assert.equal(parsedURL.protocol, 'chrome:')
assert.equal(parsedURL.hostname, 'pdf-viewer')
assert.equal(parsedURL.query.src, pdfSourceWithParams)
assert.equal(parsedURL.query.b, undefined)
assert(parsedURL.search.endsWith('%3Fa%3D1%26b%3D2'))
assert.equal(w.webContents.getTitle(), 'cat.pdf')
})
w.webContents.loadURL(pdfSourceWithParams)
})
it('should download a pdf when plugins are disabled', (done) => {
createBrowserWindow({plugins: false, preload: 'preload-pdf-loaded.js'})
ipcRenderer.sendSync('set-download-option', false, false)
ipcRenderer.once('download-done', (event, state, url, mimeType, receivedBytes, totalBytes, disposition, filename) => {
assert.equal(state, 'completed')
assert.equal(filename, 'cat.pdf')
assert.equal(mimeType, 'application/pdf')
fs.unlinkSync(path.join(fixtures, 'mock.pdf'))
done()
})
w.webContents.loadURL(pdfSource)
})
it('should not open when pdf is requested as sub resource', (done) => {
fetch(pdfSource).then((res) => {
assert.equal(res.status, 200)
assert.notEqual(document.title, 'cat.pdf')
done()
}).catch((e) => done(e))
})
it('opens when loading a pdf resource in a iframe', (done) => {
testPDFIsLoadedInSubFrame('pdf-in-iframe.html', 'preload-pdf-loaded-in-subframe.js', done)
})
it('opens when loading a pdf resource in a nested iframe', (done) => {
testPDFIsLoadedInSubFrame('pdf-in-nested-iframe.html', 'preload-pdf-loaded-in-nested-subframe.js', done)
})
})
describe('window.alert(message, title)', () => {
it('throws an exception when the arguments cannot be converted to strings', () => {
assert.throws(() => {
window.alert({toString: null})
}, /Cannot convert object to primitive value/)
})
})
describe('window.confirm(message, title)', () => {
it('throws an exception when the arguments cannot be converted to strings', () => {
assert.throws(() => {
window.confirm({toString: null}, 'title')
}, /Cannot convert object to primitive value/)
})
})
describe('window.history', () => {
describe('window.history.go(offset)', () => {
it('throws an exception when the argumnet cannot be converted to a string', () => {
assert.throws(() => {
window.history.go({toString: null})
}, /Cannot convert object to primitive value/)
})
})
describe('window.history.pushState', () => {
it('should push state after calling history.pushState() from the same url', (done) => {
w = new BrowserWindow({ show: false })
w.webContents.once('did-finish-load', () => {
// History should have current page by now.
assert.equal(w.webContents.length(), 1)
w.webContents.executeJavaScript('window.history.pushState({}, "")', () => {
// Initial page + pushed state
assert.equal(w.webContents.length(), 2)
done()
})
})
w.loadURL('about:blank')
})
})
})
})