test: make sure tests fail properly instead of timing out (#24316)

This commit is contained in:
Milan Burda 2020-07-01 00:10:36 +02:00 committed by GitHub
parent 451086d7f2
commit c6db47182a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
20 changed files with 1484 additions and 1367 deletions

View file

@ -209,21 +209,17 @@ describe('app module', () => {
});
describe('app.requestSingleInstanceLock', () => {
it('prevents the second launch of app', function (done) {
it('prevents the second launch of app', async function () {
this.timeout(120000);
const appPath = path.join(fixturesPath, 'api', 'singleton');
const first = cp.spawn(process.execPath, [appPath]);
first.once('exit', code => {
expect(code).to.equal(0);
});
await emittedOnce(first.stdout, 'data');
// Start second app when received output.
first.stdout.once('data', () => {
const second = cp.spawn(process.execPath, [appPath]);
second.once('exit', code => {
expect(code).to.equal(1);
done();
});
});
const [code2] = await emittedOnce(second, 'exit');
expect(code2).to.equal(1);
const [code1] = await emittedOnce(first, 'exit');
expect(code1).to.equal(0);
});
it('passes arguments to the second-instance event', async () => {
@ -1003,34 +999,28 @@ describe('app module', () => {
}
});
it('does not launch for argument following a URL', done => {
it('does not launch for argument following a URL', async () => {
const appPath = path.join(fixturesPath, 'api', 'quit-app');
// App should exit with non 123 code.
const first = cp.spawn(process.execPath, [appPath, 'electron-test:?', 'abc']);
first.once('exit', code => {
const [code] = await emittedOnce(first, 'exit');
expect(code).to.not.equal(123);
done();
});
});
it('launches successfully for argument following a file path', done => {
it('launches successfully for argument following a file path', async () => {
const appPath = path.join(fixturesPath, 'api', 'quit-app');
// App should exit with code 123.
const first = cp.spawn(process.execPath, [appPath, 'e:\\abc', 'abc']);
first.once('exit', code => {
const [code] = await emittedOnce(first, 'exit');
expect(code).to.equal(123);
done();
});
});
it('launches successfully for multiple URIs following --', done => {
it('launches successfully for multiple URIs following --', async () => {
const appPath = path.join(fixturesPath, 'api', 'quit-app');
// App should exit with code 123.
const first = cp.spawn(process.execPath, [appPath, '--', 'http://electronjs.org', 'electron-test://testdata']);
first.once('exit', code => {
const [code] = await emittedOnce(first, 'exit');
expect(code).to.equal(123);
done();
});
});
});

View file

@ -1,16 +1,16 @@
import { autoUpdater } from 'electron/main';
import { expect } from 'chai';
import { ifit, ifdescribe } from './spec-helpers';
import { emittedOnce } from './events-helpers';
ifdescribe(!process.mas)('autoUpdater module', function () {
describe('checkForUpdates', function () {
ifit(process.platform === 'win32')('emits an error on Windows if the feed URL is not set', function (done) {
autoUpdater.once('error', function (error) {
expect(error.message).to.equal('Update URL is not set');
done();
});
ifit(process.platform === 'win32')('emits an error on Windows if the feed URL is not set', async function () {
const errorEvent = emittedOnce(autoUpdater, 'error');
autoUpdater.setFeedURL({ url: '' });
autoUpdater.checkForUpdates();
const [error] = await errorEvent;
expect(error.message).to.equal('Update URL is not set');
});
});
@ -19,11 +19,10 @@ ifdescribe(!process.mas)('autoUpdater module', function () {
expect(autoUpdater.getFeedURL()).to.equal('');
});
ifit(process.platform === 'win32')('correctly fetches the previously set FeedURL', function (done) {
ifit(process.platform === 'win32')('correctly fetches the previously set FeedURL', function () {
const updateURL = 'https://fake-update.electron.io';
autoUpdater.setFeedURL({ url: updateURL });
expect(autoUpdater.getFeedURL()).to.equal(updateURL);
done();
});
});
@ -56,12 +55,11 @@ ifdescribe(!process.mas)('autoUpdater module', function () {
});
ifdescribe(process.platform === 'darwin')('on Mac', function () {
it('emits an error when the application is unsigned', done => {
autoUpdater.once('error', function (error) {
expect(error.message).equal('Could not get code signature for running application');
done();
});
it('emits an error when the application is unsigned', async () => {
const errorEvent = emittedOnce(autoUpdater, 'error');
autoUpdater.setFeedURL({ url: '' });
const [error] = await errorEvent;
expect(error.message).equal('Could not get code signature for running application');
});
it('does not throw if default is the serverType', () => {
@ -81,12 +79,11 @@ ifdescribe(!process.mas)('autoUpdater module', function () {
});
describe('quitAndInstall', () => {
ifit(process.platform === 'win32')('emits an error on Windows when no update is available', function (done) {
autoUpdater.once('error', function (error) {
expect(error.message).to.equal('No update available, can\'t quit and install');
done();
});
ifit(process.platform === 'win32')('emits an error on Windows when no update is available', async function () {
const errorEvent = emittedOnce(autoUpdater, 'error');
autoUpdater.quitAndInstall();
const [error] = await errorEvent;
expect(error.message).to.equal('No update available, can\'t quit and install');
});
});
});

View file

@ -233,17 +233,16 @@ describe('BrowserView module', () => {
});
describe('window.open()', () => {
it('works in BrowserView', (done) => {
it('works in BrowserView', async () => {
view = new BrowserView();
w.setBrowserView(view);
view.webContents.once('new-window', (e, url, frameName, disposition, options, additionalFeatures) => {
e.preventDefault();
const newWindow = emittedOnce(view.webContents, 'new-window');
view.webContents.once('new-window', event => event.preventDefault());
view.webContents.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
const [, url, frameName,,, additionalFeatures] = await newWindow;
expect(url).to.equal('http://host/');
expect(frameName).to.equal('host');
expect(additionalFeatures[0]).to.equal('this-is-not-a-standard-feature');
done();
});
view.webContents.loadFile(path.join(fixtures, 'pages', 'window-open.html'));
});
});
});

File diff suppressed because it is too large Load diff

View file

@ -4,7 +4,7 @@ import * as path from 'path';
import { AddressInfo } from 'net';
import { BrowserWindow } from 'electron/main';
import { closeAllWindows } from './window-helpers';
import { emittedOnce } from './events-helpers';
import { emittedOnce, emittedUntil } from './events-helpers';
describe('debugger module', () => {
const fixtures = path.resolve(__dirname, '..', 'spec', 'fixtures');
@ -21,18 +21,11 @@ describe('debugger module', () => {
afterEach(closeAllWindows);
describe('debugger.attach', () => {
it('succeeds when devtools is already open', done => {
w.webContents.on('did-finish-load', () => {
it('succeeds when devtools is already open', async () => {
await w.webContents.loadURL('about:blank');
w.webContents.openDevTools();
try {
w.webContents.debugger.attach();
} catch (err) {
done(`unexpected error : ${err}`);
}
expect(w.webContents.debugger.isAttached()).to.be.true();
done();
});
w.webContents.loadURL('about:blank');
});
it('fails when protocol version is not supported', done => {
@ -44,49 +37,33 @@ describe('debugger module', () => {
}
});
it('attaches when no protocol version is specified', done => {
try {
it('attaches when no protocol version is specified', async () => {
w.webContents.debugger.attach();
} catch (err) {
done(`unexpected error : ${err}`);
}
expect(w.webContents.debugger.isAttached()).to.be.true();
done();
});
});
describe('debugger.detach', () => {
it('fires detach event', (done) => {
w.webContents.debugger.on('detach', (e, reason) => {
it('fires detach event', async () => {
const detach = emittedOnce(w.webContents.debugger, 'detach');
w.webContents.debugger.attach();
w.webContents.debugger.detach();
const [, reason] = await detach;
expect(reason).to.equal('target closed');
expect(w.webContents.debugger.isAttached()).to.be.false();
done();
});
try {
w.webContents.debugger.attach();
} catch (err) {
done(`unexpected error : ${err}`);
}
w.webContents.debugger.detach();
});
it('doesn\'t disconnect an active devtools session', done => {
it('doesn\'t disconnect an active devtools session', async () => {
w.webContents.loadURL('about:blank');
try {
const detach = emittedOnce(w.webContents.debugger, 'detach');
w.webContents.debugger.attach();
} catch (err) {
return done(`unexpected error : ${err}`);
}
w.webContents.openDevTools();
w.webContents.once('devtools-opened', () => {
w.webContents.debugger.detach();
});
w.webContents.debugger.on('detach', () => {
await detach;
expect(w.webContents.debugger.isAttached()).to.be.false();
expect((w as any).devToolsWebContents.isDestroyed()).to.be.false();
done();
});
});
});
@ -130,33 +107,20 @@ describe('debugger module', () => {
w.webContents.debugger.detach();
});
it('fires message event', done => {
it('fires message event', async () => {
const url = process.platform !== 'win32'
? `file://${path.join(fixtures, 'pages', 'a.html')}`
: `file:///${path.join(fixtures, 'pages', 'a.html').replace(/\\/g, '/')}`;
w.webContents.loadURL(url);
try {
w.webContents.debugger.attach();
} catch (err) {
done(`unexpected error : ${err}`);
}
w.webContents.debugger.on('message', (e, method, params) => {
if (method === 'Console.messageAdded') {
try {
expect(params.message.level).to.equal('log');
expect(params.message.url).to.equal(url);
expect(params.message.text).to.equal('a');
done();
} catch (e) {
done(e);
} finally {
w.webContents.debugger.detach();
}
}
});
const message = emittedUntil(w.webContents.debugger, 'message',
(event: Electron.Event, method: string) => method === 'Console.messageAdded');
w.webContents.debugger.sendCommand('Console.enable');
const [,, params] = await message;
w.webContents.debugger.detach();
expect((params as any).message.level).to.equal('log');
expect((params as any).message.url).to.equal(url);
expect((params as any).message.text).to.equal('a');
});
it('returns error message when command fails', async () => {

View file

@ -43,9 +43,13 @@ describe('MenuItems', () => {
const menu = Menu.buildFromTemplate([{
label: 'text',
click: (item) => {
try {
expect(item.constructor.name).to.equal('MenuItem');
expect(item.label).to.equal('text');
done();
} catch (e) {
done(e);
}
}
}]);
menu._executeCommand({}, menu.items[0].commandId);

View file

@ -305,8 +305,12 @@ describe('protocol module', () => {
it('can access request headers', (done) => {
protocol.registerHttpProtocol(protocolName, (request) => {
try {
expect(request).to.have.property('headers');
done();
} catch (e) {
done(e);
}
});
ajax(protocolName + '://fake-host');
});
@ -597,8 +601,12 @@ describe('protocol module', () => {
it('can access request headers', (done) => {
protocol.interceptHttpProtocol('http', (request) => {
try {
expect(request).to.have.property('headers');
done();
} catch (e) {
done(e);
}
});
ajax('http://fake-host');
});

View file

@ -344,7 +344,7 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
});
describe('remote objects registry', () => {
it('does not dereference until the render view is deleted (regression)', (done) => {
it('does not dereference until the render view is deleted (regression)', async () => {
const w = new BrowserWindow({
show: false,
webPreferences: {
@ -353,12 +353,10 @@ ifdescribe(features.isRemoteModuleEnabled())('remote module', () => {
}
});
ipcMain.once('error-message', (event, message) => {
expect(message).to.match(/^Cannot call method 'getURL' on missing remote object/);
done();
});
const message = emittedOnce(ipcMain, 'error-message');
w.loadFile(path.join(fixtures, 'api', 'render-view-deleted.html'));
const [, msg] = await message;
expect(msg).to.match(/^Cannot call method 'getURL' on missing remote object/);
});
});

View file

@ -423,16 +423,24 @@ describe('session module', () => {
</html>`;
protocol.registerStringProtocol(scheme, (request, callback) => {
try {
if (request.method === 'GET') {
callback({ data: content, mimeType: 'text/html' });
} else if (request.method === 'POST') {
const uuid = request.uploadData![1].blobUUID;
expect(uuid).to.be.a('string');
session.defaultSession.getBlobData(uuid!).then(result => {
try {
expect(result.toString()).to.equal(postData);
done();
} catch (e) {
done(e);
}
});
}
} catch (e) {
done(e);
}
});
const w = new BrowserWindow({ show: false });
w.loadURL(url);
@ -618,8 +626,12 @@ describe('session module', () => {
session.defaultSession.once('will-download', function (e, item) {
item.savePath = downloadFilePath;
item.on('done', function (e, state) {
try {
assertDownload(state, item);
done();
} catch (e) {
done(e);
}
});
});
session.defaultSession.downloadURL(`${url}:${port}`);
@ -631,8 +643,12 @@ describe('session module', () => {
w.webContents.session.once('will-download', function (e, item) {
item.savePath = downloadFilePath;
item.on('done', function (e, state) {
try {
assertDownload(state, item);
done();
} catch (e) {
done(e);
}
});
});
w.webContents.downloadURL(`${url}:${port}`);
@ -649,8 +665,12 @@ describe('session module', () => {
w.webContents.session.once('will-download', function (e, item) {
item.savePath = downloadFilePath;
item.on('done', function (e, state) {
try {
assertDownload(state, item, true);
done();
} catch (e) {
done(e);
}
});
});
w.webContents.downloadURL(`${protocolName}://item`);
@ -687,6 +707,7 @@ describe('session module', () => {
w.webContents.session.once('will-download', function (e, item) {
item.savePath = downloadFilePath;
item.on('done', function (e, state) {
try {
expect(state).to.equal('cancelled');
expect(item.getFilename()).to.equal('mock.pdf');
expect(item.getMimeType()).to.equal('application/pdf');
@ -694,6 +715,9 @@ describe('session module', () => {
expect(item.getTotalBytes()).to.equal(mockPDF.length);
expect(item.getContentDisposition()).to.equal(contentDisposition);
done();
} catch (e) {
done(e);
}
});
item.cancel();
});
@ -712,8 +736,12 @@ describe('session module', () => {
w.webContents.session.once('will-download', function (e, item) {
item.savePath = downloadFilePath;
item.on('done', function () {
try {
expect(item.getFilename()).to.equal('download.pdf');
done();
} catch (e) {
done(e);
}
});
item.cancel();
});
@ -744,8 +772,12 @@ describe('session module', () => {
item.setSavePath(filePath);
item.setSaveDialogOptions(options);
item.on('done', function () {
try {
expect(item.getSaveDialogOptions()).to.deep.equal(options);
done();
} catch (e) {
done(e);
}
});
item.cancel();
});
@ -761,8 +793,12 @@ describe('session module', () => {
item.resume();
}
item.on('done', function (e, state) {
try {
expect(state).to.equal('interrupted');
done();
} catch (e) {
done(e);
}
});
});
w.webContents.downloadURL(`file://${path.join(__dirname, 'does-not-exist.txt')}`);

View file

@ -8,7 +8,7 @@ import { BrowserWindow, ipcMain, webContents, session, WebContents, app } from '
import { clipboard } from 'electron/common';
import { emittedOnce } from './events-helpers';
import { closeAllWindows } from './window-helpers';
import { ifdescribe, ifit, delay } from './spec-helpers';
import { ifdescribe, ifit, delay, defer } from './spec-helpers';
const pdfjs = require('pdfjs-dist');
const fixturesPath = path.resolve(__dirname, '..', 'spec', 'fixtures');
@ -223,29 +223,23 @@ describe('webContents module', () => {
server.close();
});
it('works after page load and during subframe load', (done) => {
w.webContents.once('did-finish-load', () => {
it('works after page load and during subframe load', async () => {
await w.loadURL(serverUrl);
// initiate a sub-frame load, then try and execute script during it
w.webContents.executeJavaScript(`
await w.webContents.executeJavaScript(`
var iframe = document.createElement('iframe')
iframe.src = '${serverUrl}/slow'
document.body.appendChild(iframe)
null // don't return the iframe
`).then(() => {
w.webContents.executeJavaScript('console.log(\'hello\')').then(() => {
done();
});
});
});
w.loadURL(serverUrl);
`);
await w.webContents.executeJavaScript('console.log(\'hello\')');
});
it('executes after page load', (done) => {
w.webContents.executeJavaScript('(() => "test")()').then(result => {
expect(result).to.equal('test');
done();
});
it('executes after page load', async () => {
const executeJavaScript = w.webContents.executeJavaScript('(() => "test")()');
w.loadURL(serverUrl);
const result = await executeJavaScript;
expect(result).to.equal('test');
});
});
});
@ -467,13 +461,11 @@ describe('webContents module', () => {
describe('getWebPreferences() API', () => {
afterEach(closeAllWindows);
it('should not crash when called for devTools webContents', (done) => {
it('should not crash when called for devTools webContents', async () => {
const w = new BrowserWindow({ show: false });
w.webContents.openDevTools();
w.webContents.once('devtools-opened', () => {
await emittedOnce(w.webContents, 'devtools-opened');
expect(w.webContents.devToolsWebContents!.getWebPreferences()).to.be.null();
done();
});
});
});
@ -652,71 +644,66 @@ describe('webContents module', () => {
});
afterEach(closeAllWindows);
it('can send keydown events', (done) => {
ipcMain.once('keydown', (event, key, code, keyCode, shiftKey, ctrlKey, altKey) => {
it('can send keydown events', async () => {
const keydown = emittedOnce(ipcMain, 'keydown');
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' });
const [, key, code, keyCode, shiftKey, ctrlKey, altKey] = await keydown;
expect(key).to.equal('a');
expect(code).to.equal('KeyA');
expect(keyCode).to.equal(65);
expect(shiftKey).to.be.false();
expect(ctrlKey).to.be.false();
expect(altKey).to.be.false();
done();
});
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' });
});
it('can send keydown events with modifiers', (done) => {
ipcMain.once('keydown', (event, key, code, keyCode, shiftKey, ctrlKey, altKey) => {
it('can send keydown events with modifiers', async () => {
const keydown = emittedOnce(ipcMain, 'keydown');
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Z', modifiers: ['shift', 'ctrl'] });
const [, key, code, keyCode, shiftKey, ctrlKey, altKey] = await keydown;
expect(key).to.equal('Z');
expect(code).to.equal('KeyZ');
expect(keyCode).to.equal(90);
expect(shiftKey).to.be.true();
expect(ctrlKey).to.be.true();
expect(altKey).to.be.false();
done();
});
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Z', modifiers: ['shift', 'ctrl'] });
});
it('can send keydown events with special keys', (done) => {
ipcMain.once('keydown', (event, key, code, keyCode, shiftKey, ctrlKey, altKey) => {
it('can send keydown events with special keys', async () => {
const keydown = emittedOnce(ipcMain, 'keydown');
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Tab', modifiers: ['alt'] });
const [, key, code, keyCode, shiftKey, ctrlKey, altKey] = await keydown;
expect(key).to.equal('Tab');
expect(code).to.equal('Tab');
expect(keyCode).to.equal(9);
expect(shiftKey).to.be.false();
expect(ctrlKey).to.be.false();
expect(altKey).to.be.true();
done();
});
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Tab', modifiers: ['alt'] });
});
it('can send char events', (done) => {
ipcMain.once('keypress', (event, key, code, keyCode, shiftKey, ctrlKey, altKey) => {
it('can send char events', async () => {
const keypress = emittedOnce(ipcMain, 'keypress');
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' });
w.webContents.sendInputEvent({ type: 'char', keyCode: 'A' });
const [, key, code, keyCode, shiftKey, ctrlKey, altKey] = await keypress;
expect(key).to.equal('a');
expect(code).to.equal('KeyA');
expect(keyCode).to.equal(65);
expect(shiftKey).to.be.false();
expect(ctrlKey).to.be.false();
expect(altKey).to.be.false();
done();
});
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' });
w.webContents.sendInputEvent({ type: 'char', keyCode: 'A' });
});
it('can send char events with modifiers', (done) => {
ipcMain.once('keypress', (event, key, code, keyCode, shiftKey, ctrlKey, altKey) => {
it('can send char events with modifiers', async () => {
const keypress = emittedOnce(ipcMain, 'keypress');
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Z' });
w.webContents.sendInputEvent({ type: 'char', keyCode: 'Z', modifiers: ['shift', 'ctrl'] });
const [, key, code, keyCode, shiftKey, ctrlKey, altKey] = await keypress;
expect(key).to.equal('Z');
expect(code).to.equal('KeyZ');
expect(keyCode).to.equal(90);
expect(shiftKey).to.be.true();
expect(ctrlKey).to.be.true();
expect(altKey).to.be.false();
done();
});
w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Z' });
w.webContents.sendInputEvent({ type: 'char', keyCode: 'Z', modifiers: ['shift', 'ctrl'] });
});
});
@ -953,6 +940,7 @@ describe('webContents module', () => {
e.sender.send(`${host}-zoom-set`);
});
ipcMain.on('host1-zoom-level', (e) => {
try {
const zoomLevel = e.sender.getZoomLevel();
const expectedZoomLevel = hostZoomMap.host1;
expect(zoomLevel).to.equal(expectedZoomLevel);
@ -961,38 +949,45 @@ describe('webContents module', () => {
} else {
w.loadURL(`${scheme}://host2`);
}
} catch (e) {
done(e);
}
});
ipcMain.once('host2-zoom-level', (e) => {
try {
const zoomLevel = e.sender.getZoomLevel();
const expectedZoomLevel = hostZoomMap.host2;
expect(zoomLevel).to.equal(expectedZoomLevel);
finalNavigation = true;
w.webContents.goBack();
} catch (e) {
done(e);
}
});
w.loadURL(`${scheme}://host1`);
});
it('can propagate zoom level across same session', (done) => {
it('can propagate zoom level across same session', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
const w2 = new BrowserWindow({ show: false });
w2.webContents.on('did-finish-load', () => {
defer(() => {
w2.setClosable(true);
w2.close();
});
await w.loadURL(`${scheme}://host3`);
w.webContents.zoomLevel = hostZoomMap.host3;
await w2.loadURL(`${scheme}://host3`);
const zoomLevel1 = w.webContents.zoomLevel;
expect(zoomLevel1).to.equal(hostZoomMap.host3);
const zoomLevel2 = w2.webContents.zoomLevel;
expect(zoomLevel1).to.equal(zoomLevel2);
w2.setClosable(true);
w2.close();
done();
});
w.webContents.on('did-finish-load', () => {
w.webContents.zoomLevel = hostZoomMap.host3;
w2.loadURL(`${scheme}://host3`);
});
w.loadURL(`${scheme}://host3`);
});
it('cannot propagate zoom level across different session', (done) => {
it('cannot propagate zoom level across different session', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
const w2 = new BrowserWindow({
show: false,
@ -1004,7 +999,16 @@ describe('webContents module', () => {
protocol.registerStringProtocol(scheme, (request, callback) => {
callback('hello');
});
w2.webContents.on('did-finish-load', () => {
defer(() => {
w2.setClosable(true);
w2.close();
});
await w.loadURL(`${scheme}://host3`);
w.webContents.zoomLevel = hostZoomMap.host3;
await w2.loadURL(`${scheme}://host3`);
const zoomLevel1 = w.webContents.zoomLevel;
expect(zoomLevel1).to.equal(hostZoomMap.host3);
@ -1013,15 +1017,6 @@ describe('webContents module', () => {
expect(zoomLevel1).to.not.equal(zoomLevel2);
protocol.unregisterProtocol(scheme);
w2.setClosable(true);
w2.close();
done();
});
w.webContents.on('did-finish-load', () => {
w.webContents.zoomLevel = hostZoomMap.host3;
w2.loadURL(`${scheme}://host3`);
});
w.loadURL(`${scheme}://host3`);
});
it('can persist when it contains iframe', (done) => {
@ -1036,12 +1031,17 @@ describe('webContents module', () => {
const content = `<iframe src=${url}></iframe>`;
w.webContents.on('did-frame-finish-load', (e, isMainFrame) => {
if (!isMainFrame) {
try {
const zoomLevel = w.webContents.zoomLevel;
expect(zoomLevel).to.equal(2.0);
w.webContents.zoomLevel = 0;
server.close();
done();
} catch (e) {
done(e);
} finally {
server.close();
}
}
});
w.webContents.on('dom-ready', () => {
@ -1051,30 +1051,25 @@ describe('webContents module', () => {
});
});
it('cannot propagate when used with webframe', (done) => {
it('cannot propagate when used with webframe', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
let finalZoomLevel = 0;
const w2 = new BrowserWindow({
show: false
});
w2.webContents.on('did-finish-load', () => {
const zoomLevel1 = w.webContents.zoomLevel;
expect(zoomLevel1).to.equal(finalZoomLevel);
const w2 = new BrowserWindow({ show: false });
const temporaryZoomSet = emittedOnce(ipcMain, 'temporary-zoom-set');
w.loadFile(path.join(fixturesPath, 'pages', 'webframe-zoom.html'));
await temporaryZoomSet;
const finalZoomLevel = w.webContents.getZoomLevel();
await w2.loadFile(path.join(fixturesPath, 'pages', 'c.html'));
const zoomLevel1 = w.webContents.zoomLevel;
const zoomLevel2 = w2.webContents.zoomLevel;
expect(zoomLevel2).to.equal(0);
expect(zoomLevel1).to.not.equal(zoomLevel2);
w2.setClosable(true);
w2.close();
done();
});
ipcMain.once('temporary-zoom-set', (e) => {
const zoomLevel = e.sender.getZoomLevel();
w2.loadFile(path.join(fixturesPath, 'pages', 'c.html'));
finalZoomLevel = zoomLevel;
});
w.loadFile(path.join(fixturesPath, 'pages', 'webframe-zoom.html'));
expect(zoomLevel1).to.equal(finalZoomLevel);
expect(zoomLevel2).to.equal(0);
expect(zoomLevel1).to.not.equal(zoomLevel2);
});
describe('with unique domains', () => {
@ -1168,13 +1163,9 @@ describe('webContents module', () => {
afterEach(closeAllWindows);
it('does not emit current-render-view-deleted when speculative RVHs are deleted', (done) => {
it('does not emit current-render-view-deleted when speculative RVHs are deleted', async () => {
const w = new BrowserWindow({ show: false });
let currentRenderViewDeletedEmitted = false;
w.webContents.once('destroyed', () => {
expect(currentRenderViewDeletedEmitted).to.be.false('current-render-view-deleted was emitted');
done();
});
const renderViewDeletedHandler = () => {
currentRenderViewDeletedEmitted = true;
};
@ -1183,40 +1174,41 @@ describe('webContents module', () => {
w.webContents.removeListener('current-render-view-deleted' as any, renderViewDeletedHandler);
w.close();
});
const destroyed = emittedOnce(w.webContents, 'destroyed');
w.loadURL(`${serverUrl}/redirect-cross-site`);
await destroyed;
expect(currentRenderViewDeletedEmitted).to.be.false('current-render-view-deleted was emitted');
});
it('emits current-render-view-deleted if the current RVHs are deleted', (done) => {
it('emits current-render-view-deleted if the current RVHs are deleted', async () => {
const w = new BrowserWindow({ show: false });
let currentRenderViewDeletedEmitted = false;
w.webContents.once('destroyed', () => {
expect(currentRenderViewDeletedEmitted).to.be.true('current-render-view-deleted wasn\'t emitted');
done();
});
w.webContents.on('current-render-view-deleted' as any, () => {
currentRenderViewDeletedEmitted = true;
});
w.webContents.on('did-finish-load', () => {
w.close();
});
const destroyed = emittedOnce(w.webContents, 'destroyed');
w.loadURL(`${serverUrl}/redirect-cross-site`);
await destroyed;
expect(currentRenderViewDeletedEmitted).to.be.true('current-render-view-deleted wasn\'t emitted');
});
it('emits render-view-deleted if any RVHs are deleted', (done) => {
it('emits render-view-deleted if any RVHs are deleted', async () => {
const w = new BrowserWindow({ show: false });
let rvhDeletedCount = 0;
w.webContents.once('destroyed', () => {
const expectedRenderViewDeletedEventCount = 1;
expect(rvhDeletedCount).to.equal(expectedRenderViewDeletedEventCount, 'render-view-deleted wasn\'t emitted the expected nr. of times');
done();
});
w.webContents.on('render-view-deleted' as any, () => {
rvhDeletedCount++;
});
w.webContents.on('did-finish-load', () => {
w.close();
});
const destroyed = emittedOnce(w.webContents, 'destroyed');
w.loadURL(`${serverUrl}/redirect-cross-site`);
await destroyed;
const expectedRenderViewDeletedEventCount = 1;
expect(rvhDeletedCount).to.equal(expectedRenderViewDeletedEventCount, 'render-view-deleted wasn\'t emitted the expected nr. of times');
});
});
@ -1304,6 +1296,7 @@ describe('webContents module', () => {
const w = new BrowserWindow({ show: true });
let count = 0;
w.webContents.on('did-change-theme-color', (e, color) => {
try {
if (count === 0) {
count += 1;
expect(color).to.equal('#FFEEDD');
@ -1312,6 +1305,9 @@ describe('webContents module', () => {
expect(color).to.be.null();
done();
}
} catch (e) {
done(e);
}
});
w.loadFile(path.join(fixturesPath, 'pages', 'theme-color.html'));
});
@ -1374,9 +1370,14 @@ describe('webContents module', () => {
const w = new BrowserWindow({ show: false });
const server = http.createServer((req, res) => {
if (req.url === '/should_have_referrer') {
try {
expect(req.headers.referer).to.equal(`http://127.0.0.1:${(server.address() as AddressInfo).port}/`);
server.close();
return done();
} catch (e) {
return done(e);
} finally {
server.close();
}
}
res.end('<a id="a" href="/should_have_referrer" target="_blank">link</a>');
});
@ -1399,8 +1400,12 @@ describe('webContents module', () => {
const w = new BrowserWindow({ show: false });
const server = http.createServer((req, res) => {
if (req.url === '/should_have_referrer') {
try {
expect(req.headers.referer).to.equal(`http://127.0.0.1:${(server.address() as AddressInfo).port}/`);
return done();
} catch (e) {
return done(e);
}
}
res.end('');
});
@ -1640,8 +1645,7 @@ describe('webContents module', () => {
});
it('respects custom settings', async () => {
w.loadFile(path.join(__dirname, 'fixtures', 'api', 'print-to-pdf.html'));
await emittedOnce(w.webContents, 'did-finish-load');
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'print-to-pdf.html'));
const data = await w.webContents.printToPDF({
pageRanges: {
@ -1676,15 +1680,12 @@ describe('webContents module', () => {
describe('PictureInPicture video', () => {
afterEach(closeAllWindows);
it('works as expected', (done) => {
it('works as expected', async () => {
const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } });
w.webContents.once('did-finish-load', async () => {
await w.loadFile(path.join(fixturesPath, 'api', 'picture-in-picture.html'));
const result = await w.webContents.executeJavaScript(
`runTest(${features.isPictureInPictureEnabled()})`, true);
expect(result).to.be.true();
done();
});
w.loadFile(path.join(fixturesPath, 'api', 'picture-in-picture.html'));
});
});

View file

@ -2,6 +2,7 @@ import { expect } from 'chai';
import * as path from 'path';
import { BrowserWindow, ipcMain } from 'electron/main';
import { closeAllWindows } from './window-helpers';
import { emittedOnce } from './events-helpers';
describe('asar package', () => {
const fixtures = path.join(__dirname, '..', 'spec', 'fixtures');
@ -10,7 +11,7 @@ describe('asar package', () => {
afterEach(closeAllWindows);
describe('asar protocol', () => {
it('sets __dirname correctly', function (done) {
it('sets __dirname correctly', async function () {
after(function () {
ipcMain.removeAllListeners('dirname');
});
@ -24,14 +25,13 @@ describe('asar package', () => {
}
});
const p = path.resolve(asarDir, 'web.asar', 'index.html');
ipcMain.once('dirname', function (event, dirname) {
expect(dirname).to.equal(path.dirname(p));
done();
});
const dirnameEvent = emittedOnce(ipcMain, 'dirname');
w.loadFile(p);
const [, dirname] = await dirnameEvent;
expect(dirname).to.equal(path.dirname(p));
});
it('loads script tag in html', function (done) {
it('loads script tag in html', async function () {
after(function () {
ipcMain.removeAllListeners('ping');
});
@ -45,14 +45,13 @@ describe('asar package', () => {
}
});
const p = path.resolve(asarDir, 'script.asar', 'index.html');
const ping = emittedOnce(ipcMain, 'ping');
w.loadFile(p);
ipcMain.once('ping', function (event, message) {
const [, message] = await ping;
expect(message).to.equal('pong');
done();
});
});
it('loads video tag in html', function (done) {
it('loads video tag in html', async function () {
this.timeout(60000);
after(function () {
@ -69,14 +68,12 @@ describe('asar package', () => {
});
const p = path.resolve(asarDir, 'video.asar', 'index.html');
w.loadFile(p);
ipcMain.on('asar-video', function (event, message, error) {
const [, message, error] = await emittedOnce(ipcMain, 'asar-video');
if (message === 'ended') {
expect(error).to.be.null();
done();
} else if (message === 'error') {
done(error);
throw new Error(error);
}
});
});
});
});

View file

@ -10,7 +10,7 @@ import * as url from 'url';
import * as ChildProcess from 'child_process';
import { EventEmitter } from 'events';
import { promisify } from 'util';
import { ifit, ifdescribe, delay } from './spec-helpers';
import { ifit, ifdescribe, delay, defer } from './spec-helpers';
import { AddressInfo } from 'net';
import { PipeTransport } from './pipe-transport';
@ -257,22 +257,21 @@ describe('command line switches', () => {
});
describe('--lang switch', () => {
const currentLocale = app.getLocale();
const testLocale = (locale: string, result: string, done: () => void) => {
const testLocale = async (locale: string, result: string) => {
const appPath = path.join(fixturesPath, 'api', 'locale-check');
const electronPath = process.execPath;
let output = '';
appProcess = ChildProcess.spawn(electronPath, [appPath, `--lang=${locale}`]);
let output = '';
appProcess.stdout.on('data', (data) => { output += data; });
appProcess.stdout.on('end', () => {
await emittedOnce(appProcess.stdout, 'end');
output = output.replace(/(\r\n|\n|\r)/gm, '');
expect(output).to.equal(result);
done();
});
};
it('should set the locale', (done) => testLocale('fr', 'fr', done));
it('should not set an invalid locale', (done) => testLocale('asdfkl', currentLocale, done));
it('should set the locale', async () => testLocale('fr', 'fr'));
it('should not set an invalid locale', async () => testLocale('asdfkl', currentLocale));
});
describe('--remote-debugging-pipe switch', () => {
@ -330,10 +329,15 @@ describe('command line switches', () => {
appProcess!.stderr.removeAllListeners('data');
const port = m[1];
http.get(`http://127.0.0.1:${port}`, (res) => {
res.destroy();
try {
expect(res.statusCode).to.eql(200);
expect(parseInt(res.headers['content-length']!)).to.be.greaterThan(0);
done();
} catch (e) {
done(e);
} finally {
res.destroy();
}
});
}
});
@ -551,7 +555,7 @@ describe('chromium features', () => {
describe('window.open', () => {
for (const show of [true, false]) {
it(`inherits parent visibility over parent {show=${show}} option`, (done) => {
it(`inherits parent visibility over parent {show=${show}} option`, async () => {
const w = new BrowserWindow({ show });
// toggle visibility
@ -561,12 +565,12 @@ describe('chromium features', () => {
w.show();
}
w.webContents.once('new-window', (e, url, frameName, disposition, options) => {
expect(options.show).to.equal(w.isVisible());
w.close();
done();
});
defer(() => { w.close(); });
const newWindow = emittedOnce(w.webContents, 'new-window');
w.loadFile(path.join(fixturesPath, 'pages', 'window-open.html'));
const [,,,, options] = await newWindow;
expect(options.show).to.equal(w.isVisible());
});
}
@ -602,7 +606,7 @@ describe('chromium features', () => {
expect(preferences.javascript).to.be.false();
});
it('handles cycles when merging the parent options into the child options', (done) => {
it('handles cycles when merging the parent options into the child options', async () => {
const foo = {} as any;
foo.bar = foo;
foo.baz = {
@ -614,7 +618,7 @@ describe('chromium features', () => {
const w = new BrowserWindow({ show: false, foo: foo } as any);
w.loadFile(path.join(fixturesPath, 'pages', 'window-open.html'));
w.webContents.once('new-window', (event, url, frameName, disposition, options) => {
const [,,,, options] = await emittedOnce(w.webContents, 'new-window');
expect(options.show).to.be.false();
expect((options as any).foo).to.deep.equal({
bar: undefined,
@ -629,8 +633,6 @@ describe('chromium features', () => {
}
}
});
done();
});
});
it('defines a window.location getter', async () => {
@ -959,44 +961,39 @@ describe('chromium features', () => {
contents = null as any;
});
it('cannot access localStorage', (done) => {
ipcMain.once('local-storage-response', (event, error) => {
expect(error).to.equal('Failed to read the \'localStorage\' property from \'Window\': Access is denied for this document.');
done();
});
it('cannot access localStorage', async () => {
const response = emittedOnce(ipcMain, 'local-storage-response');
contents.loadURL(protocolName + '://host/localStorage');
const [, error] = await response;
expect(error).to.equal('Failed to read the \'localStorage\' property from \'Window\': Access is denied for this document.');
});
it('cannot access sessionStorage', (done) => {
ipcMain.once('session-storage-response', (event, error) => {
expect(error).to.equal('Failed to read the \'sessionStorage\' property from \'Window\': Access is denied for this document.');
done();
});
it('cannot access sessionStorage', async () => {
const response = emittedOnce(ipcMain, 'session-storage-response');
contents.loadURL(`${protocolName}://host/sessionStorage`);
const [, error] = await response;
expect(error).to.equal('Failed to read the \'sessionStorage\' property from \'Window\': Access is denied for this document.');
});
it('cannot access WebSQL database', (done) => {
ipcMain.once('web-sql-response', (event, error) => {
expect(error).to.equal('Failed to execute \'openDatabase\' on \'Window\': Access to the WebDatabase API is denied in this context.');
done();
});
it('cannot access WebSQL database', async () => {
const response = emittedOnce(ipcMain, 'web-sql-response');
contents.loadURL(`${protocolName}://host/WebSQL`);
const [, error] = await response;
expect(error).to.equal('Failed to execute \'openDatabase\' on \'Window\': Access to the WebDatabase API is denied in this context.');
});
it('cannot access indexedDB', (done) => {
ipcMain.once('indexed-db-response', (event, error) => {
expect(error).to.equal('Failed to execute \'open\' on \'IDBFactory\': access to the Indexed Database API is denied in this context.');
done();
});
it('cannot access indexedDB', async () => {
const response = emittedOnce(ipcMain, 'indexed-db-response');
contents.loadURL(`${protocolName}://host/indexedDB`);
const [, error] = await response;
expect(error).to.equal('Failed to execute \'open\' on \'IDBFactory\': access to the Indexed Database API is denied in this context.');
});
it('cannot access cookie', (done) => {
ipcMain.once('cookie-response', (event, error) => {
expect(error).to.equal('Failed to set the \'cookie\' property on \'Document\': Access is denied for this document.');
done();
});
it('cannot access cookie', async () => {
const response = emittedOnce(ipcMain, 'cookie-response');
contents.loadURL(`${protocolName}://host/cookie`);
const [, error] = await response;
expect(error).to.equal('Failed to set the \'cookie\' property on \'Document\': Access is denied for this document.');
});
});
@ -1034,7 +1031,7 @@ describe('chromium features', () => {
afterEach(closeAllWindows);
const testLocalStorageAfterXSiteRedirect = (testTitle: string, extraPreferences = {}) => {
it(testTitle, (done) => {
it(testTitle, async () => {
const w = new BrowserWindow({
show: false,
...extraPreferences
@ -1047,11 +1044,8 @@ describe('chromium features', () => {
expect(url).to.equal(`${serverCrossSiteUrl}/redirected`);
redirected = true;
});
w.webContents.on('did-finish-load', () => {
await w.loadURL(`${serverUrl}/redirect-cross-site`);
expect(redirected).to.be.true('didnt redirect');
done();
});
w.loadURL(`${serverUrl}/redirect-cross-site`);
});
};
@ -1363,8 +1357,13 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', ()
server.close();
});
it('can fullscreen from out-of-process iframes (OOPIFs)', done => {
ipcMain.once('fullscreenChange', async () => {
it('can fullscreen from out-of-process iframes (OOPIFs)', async () => {
const fullscreenChange = emittedOnce(ipcMain, 'fullscreenChange');
const html =
'<iframe style="width: 0" frameborder=0 src="http://localhost:8989" allowfullscreen></iframe>';
w.loadURL(`data:text/html,${html}`);
await fullscreenChange;
const fullscreenWidth = await w.webContents.executeJavaScript(
"document.querySelector('iframe').offsetWidth"
);
@ -1380,17 +1379,13 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', ()
"document.querySelector('iframe').offsetWidth"
);
expect(width).to.equal(0);
done();
});
const html =
'<iframe style="width: 0" frameborder=0 src="http://localhost:8989" allowfullscreen></iframe>';
w.loadURL(`data:text/html,${html}`);
});
it('can fullscreen from in-process iframes', async () => {
const fullscreenChange = emittedOnce(ipcMain, 'fullscreenChange');
w.loadFile(path.join(fixturesPath, 'pages', 'fullscreen-ipif.html'));
await fullscreenChange;
it('can fullscreen from in-process iframes', done => {
ipcMain.once('fullscreenChange', async () => {
const fullscreenWidth = await w.webContents.executeJavaScript(
"document.querySelector('iframe').offsetWidth"
);
@ -1401,9 +1396,5 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', ()
"document.querySelector('iframe').offsetWidth"
);
expect(width).to.equal(0);
done();
});
w.loadFile(path.join(fixturesPath, 'pages', 'fullscreen-ipif.html'));
});
});

View file

@ -4,6 +4,7 @@ import * as fs from 'fs';
import { BrowserWindow } from 'electron/main';
import { ifdescribe, ifit } from './spec-helpers';
import { closeAllWindows } from './window-helpers';
import { emittedOnce } from './events-helpers';
import * as childProcess from 'child_process';
const Module = require('module');
@ -23,12 +24,10 @@ describe('modules support', () => {
await expect(w.webContents.executeJavaScript('{ require(\'echo\'); null }')).to.be.fulfilled();
});
ifit(features.isRunAsNodeEnabled())('can be required in node binary', function (done) {
ifit(features.isRunAsNodeEnabled())('can be required in node binary', async function () {
const child = childProcess.fork(path.join(fixtures, 'module', 'echo.js'));
child.on('message', (msg) => {
const [msg] = await emittedOnce(child, 'message');
expect(msg).to.equal('ok');
done();
});
});
ifit(process.platform === 'win32')('can be required if electron.exe is renamed', () => {

View file

@ -12,13 +12,12 @@ describe('node feature', () => {
const fixtures = path.join(__dirname, '..', 'spec', 'fixtures');
describe('child_process', () => {
describe('child_process.fork', () => {
it('Works in browser process', (done) => {
it('Works in browser process', async () => {
const child = childProcess.fork(path.join(fixtures, 'module', 'ping.js'));
child.on('message', (msg) => {
expect(msg).to.equal('message');
done();
});
const message = emittedOnce(child, 'message');
child.send('message');
const [msg] = await message;
expect(msg).to.equal('message');
});
});
});
@ -199,7 +198,7 @@ describe('node feature', () => {
child.stdout.on('data', listener);
});
it('Supports starting the v8 inspector with --inspect and a provided port', (done) => {
it('Supports starting the v8 inspector with --inspect and a provided port', async () => {
child = childProcess.spawn(process.execPath, ['--inspect=17364', path.join(fixtures, 'module', 'run-as-node.js')], {
env: { ELECTRON_RUN_AS_NODE: 'true' }
});
@ -214,18 +213,16 @@ describe('node feature', () => {
child.stderr.on('data', listener);
child.stdout.on('data', listener);
child.on('exit', () => {
await emittedOnce(child, 'exit');
cleanup();
if (/^Debugger listening on ws:/m.test(output)) {
expect(output.trim()).to.contain(':17364', 'should be listening on port 17364');
done();
} else {
done(new Error(`Unexpected output: ${output.toString()}`));
throw new Error(`Unexpected output: ${output.toString()}`);
}
});
});
it('Does not start the v8 inspector when --inspect is after a -- argument', (done) => {
it('Does not start the v8 inspector when --inspect is after a -- argument', async () => {
child = childProcess.spawn(process.execPath, [path.join(fixtures, 'module', 'noop.js'), '--', '--inspect']);
exitPromise = emittedOnce(child, 'exit');
@ -233,14 +230,11 @@ describe('node feature', () => {
const listener = (data: Buffer) => { output += data; };
child.stderr.on('data', listener);
child.stdout.on('data', listener);
child.on('exit', () => {
await emittedOnce(child, 'exit');
if (output.trim().startsWith('Debugger listening on ws://')) {
done(new Error('Inspector was started when it should not have been'));
} else {
done();
throw new Error('Inspector was started when it should not have been');
}
});
});
// IPC Electron child process not supported on Windows
ifit(process.platform !== 'win32')('does not crash when quitting with the inspector connected', function (done) {
@ -289,20 +283,17 @@ describe('node feature', () => {
});
});
it('Supports js binding', (done) => {
it('Supports js binding', async () => {
child = childProcess.spawn(process.execPath, ['--inspect', path.join(fixtures, 'module', 'inspector-binding.js')], {
env: { ELECTRON_RUN_AS_NODE: 'true' },
stdio: ['ipc']
}) as childProcess.ChildProcessWithoutNullStreams;
exitPromise = emittedOnce(child, 'exit');
child.on('message', ({ cmd, debuggerEnabled, success }) => {
if (cmd === 'assert') {
const [{ cmd, debuggerEnabled, success }] = await emittedOnce(child, 'message');
expect(cmd).to.equal('assert');
expect(debuggerEnabled).to.be.true();
expect(success).to.be.true();
done();
}
});
});
});
@ -311,17 +302,15 @@ describe('node feature', () => {
expect(result.status).to.equal(0);
});
ifit(features.isRunAsNodeEnabled())('handles Promise timeouts correctly', (done) => {
ifit(features.isRunAsNodeEnabled())('handles Promise timeouts correctly', async () => {
const scriptPath = path.join(fixtures, 'module', 'node-promise-timer.js');
const child = childProcess.spawn(process.execPath, [scriptPath], {
env: { ELECTRON_RUN_AS_NODE: 'true' }
});
emittedOnce(child, 'exit').then(([code, signal]) => {
const [code, signal] = await emittedOnce(child, 'exit');
expect(code).to.equal(0);
expect(signal).to.equal(null);
child.kill();
done();
});
});
it('performs microtask checkpoint correctly', (done) => {

View file

@ -39,70 +39,51 @@ describe('webFrame module', function () {
childFrameElement.remove();
});
it('executeJavaScript() yields results via a promise and a sync callback', done => {
it('executeJavaScript() yields results via a promise and a sync callback', async () => {
let callbackResult, callbackError;
childFrame
const executeJavaScript = childFrame
.executeJavaScript('1 + 1', (result, error) => {
callbackResult = result;
callbackError = error;
})
.then(
promiseResult => {
expect(promiseResult).to.equal(2);
done();
},
promiseError => {
done(promiseError);
}
);
});
expect(callbackResult).to.equal(2);
expect(callbackError).to.be.undefined();
const promiseResult = await executeJavaScript;
expect(promiseResult).to.equal(2);
});
it('executeJavaScriptInIsolatedWorld() yields results via a promise and a sync callback', done => {
it('executeJavaScriptInIsolatedWorld() yields results via a promise and a sync callback', async () => {
let callbackResult, callbackError;
childFrame
const executeJavaScriptInIsolatedWorld = childFrame
.executeJavaScriptInIsolatedWorld(999, [{ code: '1 + 1' }], (result, error) => {
callbackResult = result;
callbackError = error;
})
.then(
promiseResult => {
expect(promiseResult).to.equal(2);
done();
},
promiseError => {
done(promiseError);
}
);
});
expect(callbackResult).to.equal(2);
expect(callbackError).to.be.undefined();
const promiseResult = await executeJavaScriptInIsolatedWorld;
expect(promiseResult).to.equal(2);
});
it('executeJavaScript() yields errors via a promise and a sync callback', done => {
it('executeJavaScript() yields errors via a promise and a sync callback', async () => {
let callbackResult, callbackError;
childFrame
const executeJavaScript = childFrame
.executeJavaScript('thisShouldProduceAnError()', (result, error) => {
callbackResult = result;
callbackError = error;
})
.then(
promiseResult => {
done(new Error('error is expected'));
},
promiseError => {
expect(promiseError).to.be.an('error');
done();
}
);
});
expect(callbackResult).to.be.undefined();
expect(callbackError).to.be.an('error');
await expect(executeJavaScript).to.eventually.be.rejected('error is expected');
});
// executeJavaScriptInIsolatedWorld is failing to detect exec errors and is neither
@ -113,23 +94,16 @@ describe('webFrame module', function () {
// it('executeJavaScriptInIsolatedWorld() yields errors via a promise and a sync callback', done => {
// let callbackResult, callbackError
//
// childFrame
// const executeJavaScriptInIsolatedWorld = childFrame
// .executeJavaScriptInIsolatedWorld(999, [{ code: 'thisShouldProduceAnError()' }], (result, error) => {
// callbackResult = result
// callbackError = error
// })
// .then(
// promiseResult => {
// done(new Error('error is expected'))
// },
// promiseError => {
// expect(promiseError).to.be.an('error')
// done()
// }
// )
// });
//
// expect(callbackResult).to.be.undefined()
// expect(callbackError).to.be.an('error')
//
// expect(executeJavaScriptInIsolatedWorld).to.eventually.be.rejected('error is expected');
// })
it('executeJavaScript(InIsolatedWorld) can be used without a callback', async () => {

View file

@ -4,6 +4,8 @@ const fs = require('fs');
const path = require('path');
const temp = require('temp').track();
const util = require('util');
const { emittedOnce } = require('./events-helpers');
const { ifit } = require('./spec-helpers');
const nativeImage = require('electron').nativeImage;
const features = process._linkedBinding('electron_common_features');
@ -99,53 +101,77 @@ describe('asar package', function () {
it('reads a normal file', function (done) {
const p = path.join(asarDir, 'a.asar', 'file1');
fs.readFile(p, function (err, content) {
try {
expect(err).to.be.null();
expect(String(content).trim()).to.equal('file1');
done();
} catch (e) {
done(e);
}
});
});
it('reads from a empty file', function (done) {
const p = path.join(asarDir, 'empty.asar', 'file1');
fs.readFile(p, function (err, content) {
try {
expect(err).to.be.null();
expect(String(content)).to.equal('');
done();
} catch (e) {
done(e);
}
});
});
it('reads from a empty file with encoding', function (done) {
const p = path.join(asarDir, 'empty.asar', 'file1');
fs.readFile(p, 'utf8', function (err, content) {
try {
expect(err).to.be.null();
expect(content).to.equal('');
done();
} catch (e) {
done(e);
}
});
});
it('reads a linked file', function (done) {
const p = path.join(asarDir, 'a.asar', 'link1');
fs.readFile(p, function (err, content) {
try {
expect(err).to.be.null();
expect(String(content).trim()).to.equal('file1');
done();
} catch (e) {
done(e);
}
});
});
it('reads a file from linked directory', function (done) {
const p = path.join(asarDir, 'a.asar', 'link2', 'link2', 'file1');
fs.readFile(p, function (err, content) {
try {
expect(err).to.be.null();
expect(String(content).trim()).to.equal('file1');
done();
} catch (e) {
done(e);
}
});
});
it('throws ENOENT error when can not find file', function (done) {
const p = path.join(asarDir, 'a.asar', 'not-exist');
fs.readFile(p, function (err) {
try {
expect(err.code).to.equal('ENOENT');
done();
} catch (e) {
done(e);
}
});
});
});
@ -192,9 +218,13 @@ describe('asar package', function () {
const p = path.join(asarDir, 'a.asar', 'file1');
const dest = temp.path();
fs.copyFile(p, dest, function (err) {
try {
expect(err).to.be.null();
expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true();
done();
} catch (e) {
done(e);
}
});
});
@ -202,9 +232,13 @@ describe('asar package', function () {
const p = path.join(asarDir, 'unpack.asar', 'a.txt');
const dest = temp.path();
fs.copyFile(p, dest, function (err) {
try {
expect(err).to.be.null();
expect(fs.readFileSync(p).equals(fs.readFileSync(dest))).to.be.true();
done();
} catch (e) {
done(e);
}
});
});
});
@ -339,80 +373,108 @@ describe('asar package', function () {
it('returns information of root', function (done) {
const p = path.join(asarDir, 'a.asar');
fs.lstat(p, function (err, stats) {
try {
expect(err).to.be.null();
expect(stats.isFile()).to.be.false();
expect(stats.isDirectory()).to.be.true();
expect(stats.isSymbolicLink()).to.be.false();
expect(stats.size).to.equal(0);
done();
} catch (e) {
done(e);
}
});
});
it('returns information of root with stats as bigint', function (done) {
const p = path.join(asarDir, 'a.asar');
fs.lstat(p, { bigint: false }, function (err, stats) {
try {
expect(err).to.be.null();
expect(stats.isFile()).to.be.false();
expect(stats.isDirectory()).to.be.true();
expect(stats.isSymbolicLink()).to.be.false();
expect(stats.size).to.equal(0);
done();
} catch (e) {
done(e);
}
});
});
it('returns information of a normal file', function (done) {
const p = path.join(asarDir, 'a.asar', 'link2', 'file1');
fs.lstat(p, function (err, stats) {
try {
expect(err).to.be.null();
expect(stats.isFile()).to.be.true();
expect(stats.isDirectory()).to.be.false();
expect(stats.isSymbolicLink()).to.be.false();
expect(stats.size).to.equal(6);
done();
} catch (e) {
done(e);
}
});
});
it('returns information of a normal directory', function (done) {
const p = path.join(asarDir, 'a.asar', 'dir1');
fs.lstat(p, function (err, stats) {
try {
expect(err).to.be.null();
expect(stats.isFile()).to.be.false();
expect(stats.isDirectory()).to.be.true();
expect(stats.isSymbolicLink()).to.be.false();
expect(stats.size).to.equal(0);
done();
} catch (e) {
done(e);
}
});
});
it('returns information of a linked file', function (done) {
const p = path.join(asarDir, 'a.asar', 'link2', 'link1');
fs.lstat(p, function (err, stats) {
try {
expect(err).to.be.null();
expect(stats.isFile()).to.be.false();
expect(stats.isDirectory()).to.be.false();
expect(stats.isSymbolicLink()).to.be.true();
expect(stats.size).to.equal(0);
done();
} catch (e) {
done(e);
}
});
});
it('returns information of a linked directory', function (done) {
const p = path.join(asarDir, 'a.asar', 'link2', 'link2');
fs.lstat(p, function (err, stats) {
try {
expect(err).to.be.null();
expect(stats.isFile()).to.be.false();
expect(stats.isDirectory()).to.be.false();
expect(stats.isSymbolicLink()).to.be.true();
expect(stats.size).to.equal(0);
done();
} catch (e) {
done(e);
}
});
});
it('throws ENOENT error when can not find file', function (done) {
const p = path.join(asarDir, 'a.asar', 'file4');
fs.lstat(p, function (err) {
try {
expect(err.code).to.equal('ENOENT');
done();
} catch (e) {
done(e);
}
});
});
});
@ -592,9 +654,13 @@ describe('asar package', function () {
const parent = fs.realpathSync(asarDir);
const p = 'a.asar';
fs.realpath(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, p));
done();
} catch (e) {
done(e);
}
});
});
@ -602,9 +668,13 @@ describe('asar package', function () {
const parent = fs.realpathSync(asarDir);
const p = path.join('a.asar', 'file1');
fs.realpath(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, p));
done();
} catch (e) {
done(e);
}
});
});
@ -612,9 +682,13 @@ describe('asar package', function () {
const parent = fs.realpathSync(asarDir);
const p = path.join('a.asar', 'dir1');
fs.realpath(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, p));
done();
} catch (e) {
done(e);
}
});
});
@ -622,9 +696,13 @@ describe('asar package', function () {
const parent = fs.realpathSync(asarDir);
const p = path.join('a.asar', 'link2', 'link1');
fs.realpath(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, 'a.asar', 'file1'));
done();
} catch (e) {
done(e);
}
});
});
@ -632,9 +710,13 @@ describe('asar package', function () {
const parent = fs.realpathSync(asarDir);
const p = path.join('a.asar', 'link2', 'link2');
fs.realpath(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, 'a.asar', 'dir1'));
done();
} catch (e) {
done(e);
}
});
});
@ -642,9 +724,13 @@ describe('asar package', function () {
const parent = fs.realpathSync(asarDir);
const p = path.join('unpack.asar', 'a.txt');
fs.realpath(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, p));
done();
} catch (e) {
done(e);
}
});
});
@ -652,8 +738,12 @@ describe('asar package', function () {
const parent = fs.realpathSync(asarDir);
const p = path.join('a.asar', 'not-exist');
fs.realpath(path.join(parent, p), err => {
try {
expect(err.code).to.equal('ENOENT');
done();
} catch (e) {
done(e);
}
});
});
});
@ -713,9 +803,13 @@ describe('asar package', function () {
const parent = fs.realpathSync.native(asarDir);
const p = 'a.asar';
fs.realpath.native(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, p));
done();
} catch (e) {
done(e);
}
});
});
@ -723,9 +817,13 @@ describe('asar package', function () {
const parent = fs.realpathSync.native(asarDir);
const p = path.join('a.asar', 'file1');
fs.realpath.native(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, p));
done();
} catch (e) {
done(e);
}
});
});
@ -733,9 +831,13 @@ describe('asar package', function () {
const parent = fs.realpathSync.native(asarDir);
const p = path.join('a.asar', 'dir1');
fs.realpath.native(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, p));
done();
} catch (e) {
done(e);
}
});
});
@ -743,9 +845,13 @@ describe('asar package', function () {
const parent = fs.realpathSync.native(asarDir);
const p = path.join('a.asar', 'link2', 'link1');
fs.realpath.native(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, 'a.asar', 'file1'));
done();
} catch (e) {
done(e);
}
});
});
@ -753,9 +859,13 @@ describe('asar package', function () {
const parent = fs.realpathSync.native(asarDir);
const p = path.join('a.asar', 'link2', 'link2');
fs.realpath.native(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, 'a.asar', 'dir1'));
done();
} catch (e) {
done(e);
}
});
});
@ -763,9 +873,13 @@ describe('asar package', function () {
const parent = fs.realpathSync.native(asarDir);
const p = path.join('unpack.asar', 'a.txt');
fs.realpath.native(path.join(parent, p), (err, r) => {
try {
expect(err).to.be.null();
expect(r).to.equal(path.join(parent, p));
done();
} catch (e) {
done(e);
}
});
});
@ -773,8 +887,12 @@ describe('asar package', function () {
const parent = fs.realpathSync.native(asarDir);
const p = path.join('a.asar', 'not-exist');
fs.realpath.native(path.join(parent, p), err => {
try {
expect(err.code).to.equal('ENOENT');
done();
} catch (e) {
done(e);
}
});
});
});
@ -820,9 +938,13 @@ describe('asar package', function () {
it('reads dirs from root', function (done) {
const p = path.join(asarDir, 'a.asar');
fs.readdir(p, function (err, dirs) {
try {
expect(err).to.be.null();
expect(dirs).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']);
done();
} catch (e) {
done(e);
}
});
});
@ -830,6 +952,7 @@ describe('asar package', function () {
const p = path.join(asarDir, 'a.asar');
fs.readdir(p, { withFileTypes: true }, (err, dirs) => {
try {
expect(err).to.be.null();
for (const dir of dirs) {
expect(dir instanceof fs.Dirent).to.be.true();
@ -838,32 +961,47 @@ describe('asar package', function () {
const names = dirs.map(a => a.name);
expect(names).to.deep.equal(['dir1', 'dir2', 'dir3', 'file1', 'file2', 'file3', 'link1', 'link2', 'ping.js']);
done();
} catch (e) {
done(e);
}
});
});
it('reads dirs from a normal dir', function (done) {
const p = path.join(asarDir, 'a.asar', 'dir1');
fs.readdir(p, function (err, dirs) {
try {
expect(err).to.be.null();
expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']);
done();
} catch (e) {
done(e);
}
});
});
it('reads dirs from a linked dir', function (done) {
const p = path.join(asarDir, 'a.asar', 'link2', 'link2');
fs.readdir(p, function (err, dirs) {
try {
expect(err).to.be.null();
expect(dirs).to.deep.equal(['file1', 'file2', 'file3', 'link1', 'link2']);
done();
} catch (e) {
done(e);
}
});
});
it('throws ENOENT error when can not find file', function (done) {
const p = path.join(asarDir, 'a.asar', 'not-exist');
fs.readdir(p, function (err) {
try {
expect(err.code).to.equal('ENOENT');
done();
} catch (e) {
done(e);
}
});
});
});
@ -942,8 +1080,12 @@ describe('asar package', function () {
it('throws ENOENT error when can not find file', function (done) {
const p = path.join(asarDir, 'a.asar', 'not-exist');
fs.open(p, 'r', function (err) {
try {
expect(err.code).to.equal('ENOENT');
done();
} catch (e) {
done(e);
}
});
});
});
@ -968,8 +1110,12 @@ describe('asar package', function () {
it('throws error when calling inside asar archive', function (done) {
const p = path.join(asarDir, 'a.asar', 'not-exist');
fs.mkdir(p, function (err) {
try {
expect(err.code).to.equal('ENOTDIR');
done();
} catch (e) {
done(e);
}
});
});
});
@ -995,8 +1141,12 @@ describe('asar package', function () {
const p = path.join(asarDir, 'a.asar', 'file1');
// eslint-disable-next-line
fs.exists(p, function (exists) {
try {
expect(exists).to.be.true();
done();
} catch (e) {
done(e);
}
});
});
@ -1004,8 +1154,12 @@ describe('asar package', function () {
const p = path.join(asarDir, 'a.asar', 'not-exist');
// eslint-disable-next-line
fs.exists(p, function (exists) {
try {
expect(exists).to.be.false();
done();
} catch (e) {
done(e);
}
});
});
@ -1013,8 +1167,12 @@ describe('asar package', function () {
const p = path.join(asarDir, 'a.asar', 'file1');
// eslint-disable-next-line
util.promisify(fs.exists)(p).then(exists => {
try {
expect(exists).to.be.true();
done();
} catch (e) {
done(e);
}
});
});
@ -1022,8 +1180,12 @@ describe('asar package', function () {
const p = path.join(asarDir, 'a.asar', 'not-exist');
// eslint-disable-next-line
util.promisify(fs.exists)(p).then(exists => {
try {
expect(exists).to.be.false();
done();
} catch (e) {
done(e);
}
});
});
});
@ -1044,32 +1206,48 @@ describe('asar package', function () {
it('accesses a normal file', function (done) {
const p = path.join(asarDir, 'a.asar', 'file1');
fs.access(p, function (err) {
try {
expect(err).to.be.undefined();
done();
} catch (e) {
done(e);
}
});
});
it('throws an error when called with write mode', function (done) {
const p = path.join(asarDir, 'a.asar', 'file1');
fs.access(p, fs.constants.R_OK | fs.constants.W_OK, function (err) {
try {
expect(err.code).to.equal('EACCES');
done();
} catch (e) {
done(e);
}
});
});
it('throws an error when called on non-existent file', function (done) {
const p = path.join(asarDir, 'a.asar', 'not-exist');
fs.access(p, function (err) {
try {
expect(err.code).to.equal('ENOENT');
done();
} catch (e) {
done(e);
}
});
});
it('allows write mode for unpacked files', function (done) {
const p = path.join(asarDir, 'unpack.asar', 'a.txt');
fs.access(p, fs.constants.R_OK | fs.constants.W_OK, function (err) {
try {
expect(err).to.be.null();
done();
} catch (e) {
done(e);
}
});
});
});
@ -1136,8 +1314,12 @@ describe('asar package', function () {
it('opens a normal js file', function (done) {
const child = ChildProcess.fork(path.join(asarDir, 'a.asar', 'ping.js'));
child.on('message', function (msg) {
try {
expect(msg).to.equal('message');
done();
} catch (e) {
done(e);
}
});
child.send('message');
});
@ -1146,8 +1328,12 @@ describe('asar package', function () {
const file = path.join(asarDir, 'a.asar', 'file1');
const child = ChildProcess.fork(path.join(fixtures, 'module', 'asar.js'));
child.on('message', function (content) {
try {
expect(content).to.equal(fs.readFileSync(file).toString());
done();
} catch (e) {
done(e);
}
});
child.send(file);
});
@ -1158,9 +1344,13 @@ describe('asar package', function () {
it('should not try to extract the command if there is a reference to a file inside an .asar', function (done) {
ChildProcess.exec('echo ' + echo + ' foo bar', function (error, stdout) {
try {
expect(error).to.be.null();
expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n');
done();
} catch (e) {
done(e);
}
});
});
@ -1175,9 +1365,13 @@ describe('asar package', function () {
const echo = path.join(asarDir, 'echo.asar', 'echo');
it('should not try to extract the command if there is a reference to a file inside an .asar', function (done) {
try {
const stdout = ChildProcess.execSync('echo ' + echo + ' foo bar');
expect(stdout.toString().replace(/\r/g, '')).to.equal(echo + ' foo bar\n');
done();
} catch (e) {
done(e);
}
});
});
@ -1194,21 +1388,28 @@ describe('asar package', function () {
it('executes binaries', function (done) {
execFile(echo, ['test'], function (error, stdout) {
try {
expect(error).to.be.null();
expect(stdout).to.equal('test\n');
done();
} catch (e) {
done(e);
}
});
});
it('executes binaries without callback', function (done) {
const process = execFile(echo, ['test']);
process.on('close', function (code) {
try {
expect(code).to.equal(0);
done();
} catch (e) {
done(e);
}
});
process.on('error', function () {
expect.fail();
done();
done('error');
});
});
@ -1351,9 +1552,13 @@ describe('asar package', function () {
}
});
forked.on('message', function (stats) {
try {
expect(stats.isFile).to.be.true();
expect(stats.size).to.equal(778);
done();
} catch (e) {
done(e);
}
});
});
@ -1370,10 +1575,14 @@ describe('asar package', function () {
output += data;
});
spawned.stdout.on('close', function () {
try {
const stats = JSON.parse(output);
expect(stats.isFile).to.be.true();
expect(stats.size).to.equal(778);
done();
} catch (e) {
done(e);
}
});
});
});
@ -1383,32 +1592,48 @@ describe('asar package', function () {
it('can request a file in package', function (done) {
const p = path.resolve(asarDir, 'a.asar', 'file1');
$.get('file://' + p, function (data) {
try {
expect(data.trim()).to.equal('file1');
done();
} catch (e) {
done(e);
}
});
});
it('can request a file in package with unpacked files', function (done) {
const p = path.resolve(asarDir, 'unpack.asar', 'a.txt');
$.get('file://' + p, function (data) {
try {
expect(data.trim()).to.equal('a');
done();
} catch (e) {
done(e);
}
});
});
it('can request a linked file in package', function (done) {
const p = path.resolve(asarDir, 'a.asar', 'link2', 'link1');
$.get('file://' + p, function (data) {
try {
expect(data.trim()).to.equal('file1');
done();
} catch (e) {
done(e);
}
});
});
it('can request a file in filesystem', function (done) {
const p = path.resolve(asarDir, 'file');
$.get('file://' + p, function (data) {
try {
expect(data.trim()).to.equal('file');
done();
} catch (e) {
done(e);
}
});
});
@ -1417,8 +1642,12 @@ describe('asar package', function () {
$.ajax({
url: 'file://' + p,
error: function (err) {
try {
expect(err.status).to.equal(404);
done();
} catch (e) {
done(e);
}
}
});
});
@ -1433,18 +1662,12 @@ describe('asar package', function () {
expect(stats.isFile()).to.be.true();
});
it('is available in forked scripts', function (done) {
if (!features.isRunAsNodeEnabled()) {
this.skip();
done();
}
ifit(features.isRunAsNodeEnabled())('is available in forked scripts', async function () {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'original-fs.js'));
child.on('message', function (msg) {
expect(msg).to.equal('object');
done();
});
const message = emittedOnce(child, 'message');
child.send('message');
const [msg] = await message;
expect(msg).to.equal('object');
});
it('can be used with streams', () => {

View file

@ -6,8 +6,9 @@ const ws = require('ws');
const url = require('url');
const ChildProcess = require('child_process');
const { ipcRenderer } = require('electron');
const { emittedOnce } = require('./events-helpers');
const { emittedOnce, waitForEvent } = require('./events-helpers');
const { resolveGetters } = require('./expect-helpers');
const { ifdescribe, delay } = require('./spec-helpers');
const features = process._linkedBinding('electron_common_features');
/* Most of the APIs here don't use standard callbacks */
@ -15,14 +16,6 @@ const features = process._linkedBinding('electron_common_features');
describe('chromium feature', () => {
const fixtures = path.resolve(__dirname, 'fixtures');
let listener = null;
afterEach(() => {
if (listener != null) {
window.removeEventListener('message', listener);
}
listener = null;
});
describe('heap snapshot', () => {
it('does not crash', function () {
@ -46,58 +39,34 @@ describe('chromium feature', () => {
});
});
describe('navigator.geolocation', () => {
before(function () {
if (!features.isFakeLocationProviderEnabled()) {
return this.skip();
}
});
it('returns position when permission is granted', (done) => {
navigator.geolocation.getCurrentPosition((position) => {
ifdescribe(features.isFakeLocationProviderEnabled())('navigator.geolocation', () => {
it('returns position when permission is granted', async () => {
const position = await new Promise((resolve, reject) => navigator.geolocation.getCurrentPosition(resolve, reject));
expect(position).to.have.a.property('coords');
expect(position).to.have.a.property('timestamp');
done();
}, (error) => {
done(error);
});
});
});
describe('window.open', () => {
it('accepts "nodeIntegration" as feature', (done) => {
let b = null;
listener = (event) => {
expect(event.data.isProcessGlobalUndefined).to.be.true();
it('accepts "nodeIntegration" as feature', async () => {
const message = waitForEvent(window, 'message');
const b = window.open(`file://${fixtures}/pages/window-opener-node.html`, '', 'nodeIntegration=no,show=no');
const event = await message;
b.close();
done();
};
window.addEventListener('message', listener);
b = window.open(`file://${fixtures}/pages/window-opener-node.html`, '', 'nodeIntegration=no,show=no');
expect(event.data.isProcessGlobalUndefined).to.be.true();
});
it('inherit options of parent window', (done) => {
let b = null;
listener = (event) => {
it('inherit options of parent window', async () => {
const message = waitForEvent(window, 'message');
const b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no');
const event = await message;
b.close();
const width = outerWidth;
const height = outerHeight;
expect(event.data).to.equal(`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 = null;
listener = (event) => {
expect(event.data.isProcessGlobalUndefined).to.be.true();
b.close();
done();
};
window.addEventListener('message', listener);
it('disables node integration when it is disabled on the parent window', async () => {
const windowUrl = require('url').format({
pathname: `${fixtures}/pages/window-opener-no-node-integration.html`,
protocol: 'file',
@ -106,18 +75,14 @@ describe('chromium feature', () => {
},
slashes: true
});
b = window.open(windowUrl, '', 'nodeIntegration=no,show=no');
const message = waitForEvent(window, 'message');
const b = window.open(windowUrl, '', 'nodeIntegration=no,show=no');
const event = await message;
b.close();
expect(event.data.isProcessGlobalUndefined).to.be.true();
});
it('disables the <webview> tag when it is disabled on the parent window', (done) => {
let b = null;
listener = (event) => {
expect(event.data.isWebViewGlobalUndefined).to.be.true();
b.close();
done();
};
window.addEventListener('message', listener);
it('disables the <webview> tag when it is disabled on the parent window', async () => {
const windowUrl = require('url').format({
pathname: `${fixtures}/pages/window-opener-no-webview-tag.html`,
protocol: 'file',
@ -126,22 +91,23 @@ describe('chromium feature', () => {
},
slashes: true
});
b = window.open(windowUrl, '', 'webviewTag=no,nodeIntegration=yes,show=no');
const message = waitForEvent(window, 'message');
const b = window.open(windowUrl, '', 'webviewTag=no,nodeIntegration=yes,show=no');
const event = await message;
b.close();
expect(event.data.isWebViewGlobalUndefined).to.be.true();
});
it('does not override child options', (done) => {
let b = null;
it('does not override child options', async () => {
const size = {
width: 350,
height: 450
};
listener = (event) => {
expect(event.data).to.equal(`size: ${size.width} ${size.height}`);
const message = waitForEvent(window, 'message');
const b = window.open(`file://${fixtures}/pages/window-open-size.html`, '', 'show=no,width=' + size.width + ',height=' + size.height);
const event = await message;
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);
expect(event.data).to.equal(`size: ${size.width} ${size.height}`);
});
it('throws an exception when the arguments cannot be converted to strings', () => {
@ -164,15 +130,12 @@ describe('chromium feature', () => {
});
describe('window.opener', () => {
it('is not null for window opened by window.open', (done) => {
let b = null;
listener = (event) => {
expect(event.data).to.equal('object');
it('is not null for window opened by window.open', async () => {
const message = waitForEvent(window, 'message');
const b = window.open(`file://${fixtures}/pages/window-opener.html`, '', 'show=no');
const event = await message;
b.close();
done();
};
window.addEventListener('message', listener);
b = window.open(`file://${fixtures}/pages/window-opener.html`, '', 'show=no');
expect(event.data).to.equal('object');
});
});
@ -187,26 +150,21 @@ describe('chromium feature', () => {
});
describe('window.opener.postMessage', () => {
it('sets source and origin correctly', (done) => {
let b = null;
listener = (event) => {
window.removeEventListener('message', listener);
it('sets source and origin correctly', async () => {
const message = waitForEvent(window, 'message');
const b = window.open(`file://${fixtures}/pages/window-opener-postMessage.html`, '', 'show=no');
const event = await message;
try {
expect(event.source).to.deep.equal(b);
b.close();
expect(event.origin).to.equal('file://');
done();
};
window.addEventListener('message', listener);
b = window.open(`file://${fixtures}/pages/window-opener-postMessage.html`, '', 'show=no');
} finally {
b.close();
}
});
it('supports windows opened from a <webview>', (done) => {
it('supports windows opened from a <webview>', async () => {
const webview = new WebView();
webview.addEventListener('console-message', (e) => {
webview.remove();
expect(e.message).to.equal('message');
done();
});
const consoleMessage = waitForEvent(webview, 'console-message');
webview.allowpopups = true;
webview.src = url.format({
pathname: `${fixtures}/pages/webview-opener-postMessage.html`,
@ -217,6 +175,9 @@ describe('chromium feature', () => {
slashes: true
});
document.body.appendChild(webview);
const event = await consoleMessage;
webview.remove();
expect(event.message).to.equal('message');
});
describe('targetOrigin argument', () => {
@ -239,16 +200,12 @@ describe('chromium feature', () => {
server.close();
});
it('delivers messages that match the origin', (done) => {
let b = null;
listener = (event) => {
window.removeEventListener('message', listener);
it('delivers messages that match the origin', async () => {
const message = waitForEvent(window, 'message');
const b = window.open(serverURL, '', 'show=no');
const event = await message;
b.close();
expect(event.data).to.equal('deliver');
done();
};
window.addEventListener('message', listener);
b = window.open(serverURL, '', 'show=no');
});
});
});
@ -273,71 +230,63 @@ describe('chromium feature', () => {
});
describe('web workers', () => {
it('Worker can work', (done) => {
it('Worker can work', async () => {
const worker = new Worker('../fixtures/workers/worker.js');
const message = 'ping';
worker.onmessage = (event) => {
expect(event.data).to.equal(message);
worker.terminate();
done();
};
const eventPromise = new Promise((resolve) => { worker.onmessage = resolve; });
worker.postMessage(message);
});
it('Worker has no node integration by default', (done) => {
const worker = new Worker('../fixtures/workers/worker_node.js');
worker.onmessage = (event) => {
expect(event.data).to.equal('undefined undefined undefined undefined');
const event = await eventPromise;
worker.terminate();
done();
};
expect(event.data).to.equal(message);
});
it('Worker has node integration with nodeIntegrationInWorker', (done) => {
const webview = new WebView();
webview.addEventListener('ipc-message', (e) => {
expect(e.channel).to.equal('object function object function');
webview.remove();
done();
it('Worker has no node integration by default', async () => {
const worker = new Worker('../fixtures/workers/worker_node.js');
const event = await new Promise((resolve) => { worker.onmessage = resolve; });
worker.terminate();
expect(event.data).to.equal('undefined undefined undefined undefined');
});
it('Worker has node integration with nodeIntegrationInWorker', async () => {
const webview = new WebView();
const eventPromise = waitForEvent(webview, 'ipc-message');
webview.src = `file://${fixtures}/pages/worker.html`;
webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker');
document.body.appendChild(webview);
const event = await eventPromise;
webview.remove();
expect(event.channel).to.equal('object function object function');
});
describe('SharedWorker', () => {
it('can work', async () => {
const worker = new SharedWorker('../fixtures/workers/shared_worker.js');
const message = 'ping';
const eventPromise = new Promise((resolve) => { worker.port.onmessage = resolve; });
worker.port.postMessage(message);
const event = await eventPromise;
expect(event.data).to.equal(message);
});
it('has no node integration by default', async () => {
const worker = new SharedWorker('../fixtures/workers/shared_worker_node.js');
const event = await new Promise((resolve) => { worker.port.onmessage = resolve; });
expect(event.data).to.equal('undefined undefined undefined undefined');
});
// FIXME: disabled during chromium update due to crash in content::WorkerScriptFetchInitiator::CreateScriptLoaderOnIO
xdescribe('SharedWorker', () => {
it('can work', (done) => {
const worker = new SharedWorker('../fixtures/workers/shared_worker.js');
const message = 'ping';
worker.port.onmessage = (event) => {
expect(event.data).to.equal(message);
done();
};
worker.port.postMessage(message);
});
it('has no node integration by default', (done) => {
const worker = new SharedWorker('../fixtures/workers/shared_worker_node.js');
worker.port.onmessage = (event) => {
expect(event.data).to.equal('undefined undefined undefined undefined');
done();
};
});
it('has node integration with nodeIntegrationInWorker', (done) => {
xit('has node integration with nodeIntegrationInWorker', async () => {
const webview = new WebView();
webview.addEventListener('console-message', (e) => {
console.log(e);
});
webview.addEventListener('ipc-message', (e) => {
expect(e.channel).to.equal('object function object function');
webview.remove();
done();
});
const eventPromise = waitForEvent(webview, 'ipc-message');
webview.src = `file://${fixtures}/pages/shared_worker.html`;
webview.setAttribute('webpreferences', 'nodeIntegration, nodeIntegrationInWorker');
document.body.appendChild(webview);
const event = await eventPromise;
webview.remove();
expect(event.channel).to.equal('object function object function');
});
});
});
@ -353,13 +302,11 @@ describe('chromium feature', () => {
document.body.removeChild(iframe);
});
it('does not have node integration', (done) => {
it('does not have node integration', async () => {
iframe.src = `file://${fixtures}/pages/set-global.html`;
document.body.appendChild(iframe);
iframe.onload = () => {
await waitForEvent(iframe, 'load');
expect(iframe.contentWindow.test).to.equal('undefined undefined undefined');
done();
};
});
});
@ -367,7 +314,7 @@ describe('chromium feature', () => {
describe('DOM storage quota increase', () => {
['localStorage', 'sessionStorage'].forEach((storageName) => {
const storage = window[storageName];
it(`allows saving at least 40MiB in ${storageName}`, (done) => {
it(`allows saving at least 40MiB in ${storageName}`, async () => {
// Although JavaScript strings use UTF-16, the underlying
// storage provider may encode strings differently, muddling the
// translation between character and byte counts. However,
@ -384,11 +331,12 @@ describe('chromium feature', () => {
// failed to detect a real problem (perhaps related to DOM storage data caching)
// wherein calling `getItem` immediately after `setItem` would appear to work
// but then later (e.g. next tick) it would not.
setTimeout(() => {
await delay(1);
try {
expect(storage.getItem(testKeyName)).to.have.lengthOf(length);
} finally {
storage.removeItem(testKeyName);
done();
}, 1);
}
});
it(`throws when attempting to use more than 128MiB in ${storageName}`, () => {
expect(() => {
@ -404,11 +352,11 @@ describe('chromium feature', () => {
});
});
it('requesting persitent quota works', (done) => {
navigator.webkitPersistentStorage.requestQuota(1024 * 1024, (grantedBytes) => {
expect(grantedBytes).to.equal(1048576);
done();
it('requesting persitent quota works', async () => {
const grantedBytes = await new Promise(resolve => {
navigator.webkitPersistentStorage.requestQuota(1024 * 1024, resolve);
});
expect(grantedBytes).to.equal(1048576);
});
});

View file

@ -20,96 +20,85 @@ describe('node feature', () => {
});
describe('child_process.fork', () => {
it('works in current process', (done) => {
it('works in current process', async () => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'ping.js'));
child.on('message', msg => {
expect(msg).to.equal('message');
done();
});
const message = emittedOnce(child, 'message');
child.send('message');
const [msg] = await message;
expect(msg).to.equal('message');
});
it('preserves args', (done) => {
it('preserves args', async () => {
const args = ['--expose_gc', '-test', '1'];
const child = ChildProcess.fork(path.join(fixtures, 'module', 'process_args.js'), args);
child.on('message', (msg) => {
const message = emittedOnce(child, 'message');
child.send('message');
const [msg] = await message;
expect(args).to.deep.equal(msg.slice(2));
done();
});
child.send('message');
});
it('works in forked process', (done) => {
it('works in forked process', async () => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js'));
child.on('message', (msg) => {
expect(msg).to.equal('message');
done();
});
const message = emittedOnce(child, 'message');
child.send('message');
const [msg] = await message;
expect(msg).to.equal('message');
});
it('works in forked process when options.env is specifed', (done) => {
it('works in forked process when options.env is specifed', async () => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'fork_ping.js'), [], {
path: process.env.PATH
});
child.on('message', (msg) => {
const message = emittedOnce(child, 'message');
child.send('message');
const [msg] = await message;
expect(msg).to.equal('message');
done();
});
child.send('message');
});
it('has String::localeCompare working in script', (done) => {
it('has String::localeCompare working in script', async () => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'locale-compare.js'));
child.on('message', (msg) => {
const message = emittedOnce(child, 'message');
child.send('message');
const [msg] = await message;
expect(msg).to.deep.equal([0, -1, 1]);
done();
});
child.send('message');
});
it('has setImmediate working in script', (done) => {
it('has setImmediate working in script', async () => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'set-immediate.js'));
child.on('message', (msg) => {
expect(msg).to.equal('ok');
done();
});
const message = emittedOnce(child, 'message');
child.send('message');
const [msg] = await message;
expect(msg).to.equal('ok');
});
it('pipes stdio', (done) => {
it('pipes stdio', async () => {
const child = ChildProcess.fork(path.join(fixtures, 'module', 'process-stdout.js'), { silent: true });
let data = '';
child.stdout.on('data', (chunk) => {
data += String(chunk);
});
child.on('close', (code) => {
const [code] = await emittedOnce(child, 'close');
expect(code).to.equal(0);
expect(data).to.equal('pipes stdio');
done();
});
});
it('works when sending a message to a process forked with the --eval argument', (done) => {
it('works when sending a message to a process forked with the --eval argument', async () => {
const source = "process.on('message', (message) => { process.send(message) })";
const forked = ChildProcess.fork('--eval', [source]);
forked.once('message', (message) => {
expect(message).to.equal('hello');
done();
});
const message = emittedOnce(forked, 'message');
forked.send('hello');
const [msg] = await message;
expect(msg).to.equal('hello');
});
it('has the electron version in process.versions', (done) => {
it('has the electron version in process.versions', async () => {
const source = 'process.send(process.versions)';
const forked = ChildProcess.fork('--eval', [source]);
forked.on('message', (message) => {
const [message] = await emittedOnce(forked, 'message');
expect(message)
.to.have.own.property('electron')
.that.is.a('string')
.and.matches(/^\d+\.\d+\.\d+(\S*)?$/);
done();
});
});
});
@ -120,7 +109,7 @@ describe('node feature', () => {
if (child != null) child.kill();
});
it('supports spawning Electron as a node process via the ELECTRON_RUN_AS_NODE env var', (done) => {
it('supports spawning Electron as a node process via the ELECTRON_RUN_AS_NODE env var', async () => {
child = ChildProcess.spawn(process.execPath, [path.join(__dirname, 'fixtures', 'module', 'run-as-node.js')], {
env: {
ELECTRON_RUN_AS_NODE: true
@ -131,14 +120,12 @@ describe('node feature', () => {
child.stdout.on('data', data => {
output += data;
});
child.stdout.on('close', () => {
await emittedOnce(child.stdout, 'close');
expect(JSON.parse(output)).to.deep.equal({
processLog: process.platform === 'win32' ? 'function' : 'undefined',
processType: 'undefined',
window: 'undefined'
});
done();
});
});
});
@ -258,18 +245,15 @@ describe('node feature', () => {
}
});
it('emit error when connect to a socket path without listeners', (done) => {
it('emit error when connect to a socket path without listeners', async () => {
const socketPath = path.join(os.tmpdir(), 'atom-shell-test.sock');
const script = path.join(fixtures, 'module', 'create_socket.js');
const child = ChildProcess.fork(script, [socketPath]);
child.on('exit', (code) => {
const [code] = await emittedOnce(child, 'exit');
expect(code).to.equal(0);
const client = require('net').connect(socketPath);
client.on('error', (error) => {
const [error] = await emittedOnce(client, 'error');
expect(error.code).to.equal('ECONNREFUSED');
done();
});
});
});
});

View file

@ -1,2 +1,4 @@
exports.ifit = (condition) => (condition ? it : it.skip);
exports.ifdescribe = (condition) => (condition ? describe : describe.skip);
exports.delay = (time = 0) => new Promise(resolve => setTimeout(resolve, time));

View file

@ -4,7 +4,7 @@ const http = require('http');
const url = require('url');
const { ipcRenderer } = require('electron');
const { emittedOnce, waitForEvent } = require('./events-helpers');
const { ifdescribe, ifit } = require('./spec-helpers');
const { ifdescribe, ifit, delay } = require('./spec-helpers');
const features = process._linkedBinding('electron_common_features');
const nativeModulesEnabled = process.env.ELECTRON_SKIP_NATIVE_MODULE_TESTS;
@ -284,10 +284,15 @@ describe('<webview> tag', function () {
it('sets the referrer url', (done) => {
const referrer = 'http://github.com/';
const server = http.createServer((req, res) => {
res.end();
server.close();
try {
expect(req.headers.referer).to.equal(referrer);
done();
} catch (e) {
done(e);
} finally {
res.end();
server.close();
}
}).listen(0, '127.0.0.1', () => {
const port = server.address().port;
loadWebView(webview, {
@ -737,6 +742,7 @@ describe('<webview> tag', function () {
};
const loadListener = () => {
try {
if (loadCount === 1) {
webview.src = `file://${fixtures}/pages/base-page.html`;
} else if (loadCount === 2) {
@ -755,6 +761,9 @@ describe('<webview> tag', function () {
}
loadCount += 1;
} catch (e) {
done(e);
}
};
webview.addEventListener('ipc-message', listener);
@ -803,8 +812,12 @@ describe('<webview> tag', function () {
server.listen(0, '127.0.0.1', () => {
const port = server.address().port;
webview.addEventListener('ipc-message', (e) => {
try {
expect(e.channel).to.equal(message);
done();
} catch (e) {
done(e);
}
});
loadWebView(webview, {
nodeintegration: 'on',
@ -1042,15 +1055,14 @@ describe('<webview> tag', function () {
});
describe('will-attach-webview event', () => {
it('does not emit when src is not changed', (done) => {
it('does not emit when src is not changed', async () => {
console.log('loadWebView(webview)');
loadWebView(webview);
setTimeout(() => {
await delay();
const expectedErrorMessage =
'The WebView must be attached to the DOM ' +
'and the dom-ready event emitted before this method can be called.';
expect(() => { webview.stop(); }).to.throw(expectedErrorMessage);
done();
});
});
it('supports changing the web preferences', async () => {