diff --git a/spec/api-app-spec.ts b/spec/api-app-spec.ts index b47a59be7315..fb46131e955d 100644 --- a/spec/api-app-spec.ts +++ b/spec/api-app-spec.ts @@ -7,9 +7,9 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import { promisify } from 'util'; import { app, BrowserWindow, Menu, session, net as electronNet } from 'electron/main'; -import { emittedOnce } from './lib/events-helpers'; import { closeWindow, closeAllWindows } from './lib/window-helpers'; import { ifdescribe, ifit, listen, waitUntil } from './lib/spec-helpers'; +import { once } from 'events'; import split = require('split') const fixturesPath = path.resolve(__dirname, 'fixtures'); @@ -169,7 +169,7 @@ describe('app module', () => { if (appProcess && appProcess.stdout) { appProcess.stdout.on('data', data => { output += data; }); } - const [code] = await emittedOnce(appProcess, 'exit'); + const [code] = await once(appProcess, 'exit'); if (process.platform !== 'win32') { expect(output).to.include('Exit event with code: 123'); @@ -182,7 +182,7 @@ describe('app module', () => { const electronPath = process.execPath; appProcess = cp.spawn(electronPath, [appPath]); - const [code, signal] = await emittedOnce(appProcess, 'exit'); + const [code, signal] = await once(appProcess, 'exit'); expect(signal).to.equal(null, 'exit signal should be null, if you see this please tag @MarshallOfSound'); expect(code).to.equal(123, 'exit code should be 123, if you see this please tag @MarshallOfSound'); @@ -203,7 +203,7 @@ describe('app module', () => { if (appProcess && appProcess.stdout) { appProcess.stdout.on('data', () => appProcess!.kill()); } - const [code, signal] = await emittedOnce(appProcess, 'exit'); + const [code, signal] = await once(appProcess, 'exit'); const message = `code:\n${code}\nsignal:\n${signal}`; expect(code).to.equal(0, message); @@ -229,37 +229,37 @@ describe('app module', () => { this.timeout(120000); const appPath = path.join(fixturesPath, 'api', 'singleton-data'); const first = cp.spawn(process.execPath, [appPath]); - await emittedOnce(first.stdout, 'data'); + await once(first.stdout, 'data'); // Start second app when received output. const second = cp.spawn(process.execPath, [appPath]); - const [code2] = await emittedOnce(second, 'exit'); + const [code2] = await once(second, 'exit'); expect(code2).to.equal(1); - const [code1] = await emittedOnce(first, 'exit'); + const [code1] = await once(first, 'exit'); expect(code1).to.equal(0); }); it('returns true when setting non-existent user data folder', async function () { const appPath = path.join(fixturesPath, 'api', 'singleton-userdata'); const instance = cp.spawn(process.execPath, [appPath]); - const [code] = await emittedOnce(instance, 'exit'); + const [code] = await once(instance, 'exit'); expect(code).to.equal(0); }); async function testArgumentPassing (testArgs: SingleInstanceLockTestArgs) { const appPath = path.join(fixturesPath, 'api', 'singleton-data'); const first = cp.spawn(process.execPath, [appPath, ...testArgs.args]); - const firstExited = emittedOnce(first, 'exit'); + const firstExited = once(first, 'exit'); // Wait for the first app to boot. const firstStdoutLines = first.stdout.pipe(split()); - while ((await emittedOnce(firstStdoutLines, 'data')).toString() !== 'started') { + while ((await once(firstStdoutLines, 'data')).toString() !== 'started') { // wait. } - const additionalDataPromise = emittedOnce(firstStdoutLines, 'data'); + const additionalDataPromise = once(firstStdoutLines, 'data'); const secondInstanceArgs = [process.execPath, appPath, ...testArgs.args, '--some-switch', 'some-arg']; const second = cp.spawn(secondInstanceArgs[0], secondInstanceArgs.slice(1)); - const secondExited = emittedOnce(second, 'exit'); + const secondExited = once(second, 'exit'); const [code2] = await secondExited; expect(code2).to.equal(1); @@ -427,7 +427,7 @@ describe('app module', () => { it('is emitted when visiting a server with a self-signed cert', async () => { const w = new BrowserWindow({ show: false }); w.loadURL(secureUrl); - await emittedOnce(app, 'certificate-error'); + await once(app, 'certificate-error'); }); describe('when denied', () => { @@ -444,7 +444,7 @@ describe('app module', () => { it('causes did-fail-load', async () => { const w = new BrowserWindow({ show: false }); w.loadURL(secureUrl); - await emittedOnce(w.webContents, 'did-fail-load'); + await once(w.webContents, 'did-fail-load'); }); }); }); @@ -506,7 +506,7 @@ describe('app module', () => { afterEach(() => closeWindow(w).then(() => { w = null as any; })); it('should emit browser-window-focus event when window is focused', async () => { - const emitted = emittedOnce(app, 'browser-window-focus'); + const emitted = once(app, 'browser-window-focus'); w = new BrowserWindow({ show: false }); w.emit('focus'); const [, window] = await emitted; @@ -514,7 +514,7 @@ describe('app module', () => { }); it('should emit browser-window-blur event when window is blurred', async () => { - const emitted = emittedOnce(app, 'browser-window-blur'); + const emitted = once(app, 'browser-window-blur'); w = new BrowserWindow({ show: false }); w.emit('blur'); const [, window] = await emitted; @@ -522,14 +522,14 @@ describe('app module', () => { }); it('should emit browser-window-created event when window is created', async () => { - const emitted = emittedOnce(app, 'browser-window-created'); + const emitted = once(app, 'browser-window-created'); w = new BrowserWindow({ show: false }); const [, window] = await emitted; expect(window.id).to.equal(w.id); }); it('should emit web-contents-created event when a webContents is created', async () => { - const emitted = emittedOnce(app, 'web-contents-created'); + const emitted = once(app, 'web-contents-created'); w = new BrowserWindow({ show: false }); const [, webContents] = await emitted; expect(webContents.id).to.equal(w.webContents.id); @@ -546,7 +546,7 @@ describe('app module', () => { }); await w.loadURL('about:blank'); - const emitted = emittedOnce(app, 'renderer-process-crashed'); + const emitted = once(app, 'renderer-process-crashed'); w.webContents.executeJavaScript('process.crash()'); const [, webContents] = await emitted; @@ -564,7 +564,7 @@ describe('app module', () => { }); await w.loadURL('about:blank'); - const emitted = emittedOnce(app, 'render-process-gone'); + const emitted = once(app, 'render-process-gone'); w.webContents.executeJavaScript('process.crash()'); const [, webContents, details] = await emitted; @@ -890,7 +890,7 @@ describe('app module', () => { ifit(process.platform === 'win32')('detects disabled by TaskManager', async function () { app.setLoginItemSettings({ openAtLogin: true, name: 'additionalEntry', enabled: true, args: ['arg1'] }); const appProcess = cp.spawn('reg', [...regAddArgs, '030000000000000000000000']); - await emittedOnce(appProcess, 'exit'); + await once(appProcess, 'exit'); expect(app.getLoginItemSettings()).to.deep.equal({ openAtLogin: false, openAsHidden: false, @@ -927,12 +927,12 @@ describe('app module', () => { app.setLoginItemSettings({ openAtLogin: true, name: 'additionalEntry', enabled: false, args: ['arg1'] }); let appProcess = cp.spawn('reg', [...regAddArgs, '020000000000000000000000']); - await emittedOnce(appProcess, 'exit'); + await once(appProcess, 'exit'); expect(app.getLoginItemSettings()).to.deep.equal(expectation); app.setLoginItemSettings({ openAtLogin: true, name: 'additionalEntry', enabled: false, args: ['arg1'] }); appProcess = cp.spawn('reg', [...regAddArgs, '000000000000000000000000']); - await emittedOnce(appProcess, 'exit'); + await once(appProcess, 'exit'); expect(app.getLoginItemSettings()).to.deep.equal(expectation); }); }); @@ -1290,7 +1290,7 @@ describe('app module', () => { 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']); - const [code] = await emittedOnce(first, 'exit'); + const [code] = await once(first, 'exit'); expect(code).to.not.equal(123); }); @@ -1298,7 +1298,7 @@ describe('app module', () => { const appPath = path.join(fixturesPath, 'api', 'quit-app'); // App should exit with code 123. const first = cp.spawn(process.execPath, [appPath, 'e:\\abc', 'abc']); - const [code] = await emittedOnce(first, 'exit'); + const [code] = await once(first, 'exit'); expect(code).to.equal(123); }); @@ -1306,7 +1306,7 @@ describe('app module', () => { 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']); - const [code] = await emittedOnce(first, 'exit'); + const [code] = await once(first, 'exit'); expect(code).to.equal(123); }); }); @@ -1432,7 +1432,7 @@ describe('app module', () => { appProcess.stderr.on('data', (data) => { errorData += data; }); - const [exitCode] = await emittedOnce(appProcess, 'exit'); + const [exitCode] = await once(appProcess, 'exit'); if (exitCode === 0) { try { const [, json] = /HERE COMES THE JSON: (.+) AND THERE IT WAS/.exec(gpuInfoData)!; @@ -1949,7 +1949,7 @@ describe('default behavior', () => { it('should emit a login event on app when a WebContents hits a 401', async () => { const w = new BrowserWindow({ show: false }); w.loadURL(serverUrl); - const [, webContents] = await emittedOnce(app, 'login'); + const [, webContents] = await once(app, 'login'); expect(webContents).to.equal(w.webContents); }); }); @@ -1976,7 +1976,7 @@ async function runTestApp (name: string, ...args: any[]) { let output = ''; appProcess.stdout.on('data', (data) => { output += data; }); - await emittedOnce(appProcess.stdout, 'end'); + await once(appProcess.stdout, 'end'); return JSON.parse(output); } diff --git a/spec/api-auto-updater-spec.ts b/spec/api-auto-updater-spec.ts index 27183c96216e..fe89b97be235 100644 --- a/spec/api-auto-updater-spec.ts +++ b/spec/api-auto-updater-spec.ts @@ -1,12 +1,12 @@ import { autoUpdater } from 'electron/main'; import { expect } from 'chai'; import { ifit, ifdescribe } from './lib/spec-helpers'; -import { emittedOnce } from './lib/events-helpers'; +import { once } from 'events'; 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', async function () { - const errorEvent = emittedOnce(autoUpdater, 'error'); + const errorEvent = once(autoUpdater, 'error'); autoUpdater.setFeedURL({ url: '' }); autoUpdater.checkForUpdates(); const [error] = await errorEvent; @@ -56,7 +56,7 @@ ifdescribe(!process.mas)('autoUpdater module', function () { ifdescribe(process.platform === 'darwin' && process.arch !== 'arm64')('on Mac', function () { it('emits an error when the application is unsigned', async () => { - const errorEvent = emittedOnce(autoUpdater, 'error'); + const errorEvent = once(autoUpdater, 'error'); autoUpdater.setFeedURL({ url: '' }); const [error] = await errorEvent; expect(error.message).equal('Could not get code signature for running application'); @@ -80,7 +80,7 @@ ifdescribe(!process.mas)('autoUpdater module', function () { describe('quitAndInstall', () => { ifit(process.platform === 'win32')('emits an error on Windows when no update is available', async function () { - const errorEvent = emittedOnce(autoUpdater, 'error'); + const errorEvent = once(autoUpdater, 'error'); autoUpdater.quitAndInstall(); const [error] = await errorEvent; expect(error.message).to.equal('No update available, can\'t quit and install'); diff --git a/spec/api-browser-view-spec.ts b/spec/api-browser-view-spec.ts index c4de3dff86fe..65b70e911b53 100644 --- a/spec/api-browser-view-spec.ts +++ b/spec/api-browser-view-spec.ts @@ -1,10 +1,10 @@ import { expect } from 'chai'; import * as path from 'path'; -import { emittedOnce } from './lib/events-helpers'; import { BrowserView, BrowserWindow, screen, webContents } from 'electron/main'; import { closeWindow } from './lib/window-helpers'; import { defer, ifit, startRemoteControlApp } from './lib/spec-helpers'; import { areColorsSimilar, captureScreen, getPixelColor } from './lib/screen-helpers'; +import { once } from 'events'; describe('BrowserView module', () => { const fixtures = path.resolve(__dirname, 'fixtures'); @@ -25,13 +25,13 @@ describe('BrowserView module', () => { }); afterEach(async () => { - const p = emittedOnce(w.webContents, 'destroyed'); + const p = once(w.webContents, 'destroyed'); await closeWindow(w); w = null as any; await p; if (view && view.webContents) { - const p = emittedOnce(view.webContents, 'destroyed'); + const p = once(view.webContents, 'destroyed'); view.webContents.destroy(); view = null as any; await p; @@ -231,7 +231,7 @@ describe('BrowserView module', () => { w.addBrowserView(view); view.webContents.loadURL('about:blank'); - await emittedOnce(view.webContents, 'did-finish-load'); + await once(view.webContents, 'did-finish-load'); const w2 = new BrowserWindow({ show: false }); w2.addBrowserView(view); @@ -239,7 +239,7 @@ describe('BrowserView module', () => { w.close(); view.webContents.loadURL(`file://${fixtures}/pages/blank.html`); - await emittedOnce(view.webContents, 'did-finish-load'); + await once(view.webContents, 'did-finish-load'); // Clean up - the afterEach hook assumes the webContents on w is still alive. w = new BrowserWindow({ show: false }); @@ -326,7 +326,7 @@ describe('BrowserView module', () => { app.quit(); }); }); - const [code] = await emittedOnce(rc.process, 'exit'); + const [code] = await once(rc.process, 'exit'); expect(code).to.equal(0); }); @@ -342,7 +342,7 @@ describe('BrowserView module', () => { app.quit(); }); }); - const [code] = await emittedOnce(rc.process, 'exit'); + const [code] = await once(rc.process, 'exit'); expect(code).to.equal(0); }); }); diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window-spec.ts index 3252bc0eff84..4eb4e908c497 100644 --- a/spec/api-browser-window-spec.ts +++ b/spec/api-browser-window-spec.ts @@ -7,10 +7,12 @@ import * as http from 'http'; import * as os from 'os'; import { app, BrowserWindow, BrowserView, dialog, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents } from 'electron/main'; -import { emittedOnce, emittedUntil, emittedNTimes } from './lib/events-helpers'; -import { ifit, ifdescribe, defer, delay, listen } from './lib/spec-helpers'; +import { emittedUntil, emittedNTimes } from './lib/events-helpers'; +import { ifit, ifdescribe, defer, listen } from './lib/spec-helpers'; import { closeWindow, closeAllWindows } from './lib/window-helpers'; import { areColorsSimilar, captureScreen, HexColors, getPixelColor } from './lib/screen-helpers'; +import { once } from 'events'; +import { setTimeout } from 'timers/promises'; const features = process._linkedBinding('electron_common_features'); const fixtures = path.resolve(__dirname, 'fixtures'); @@ -61,7 +63,7 @@ describe('BrowserWindow module', () => { ifit(process.platform === 'linux')('does not crash when setting large window icons', async () => { const appPath = path.join(fixtures, 'apps', 'xwindow-icon'); const appProcess = childProcess.spawn(process.execPath, [appPath]); - await emittedOnce(appProcess, 'exit'); + await once(appProcess, 'exit'); }); it('does not crash or throw when passed an invalid icon', async () => { @@ -83,12 +85,12 @@ describe('BrowserWindow module', () => { // Keep a weak reference to the window. // eslint-disable-next-line no-undef const wr = new WeakRef(w); - await delay(); + await setTimeout(); // Do garbage collection, since |w| is not referenced in this closure // it would be gone after next call if there is no other reference. v8Util.requestGarbageCollectionForTesting(); - await delay(); + await setTimeout(); expect(wr.deref()).to.not.be.undefined(); }); }); @@ -104,7 +106,7 @@ describe('BrowserWindow module', () => { }); it('should work if called when a messageBox is showing', async () => { - const closed = emittedOnce(w, 'closed'); + const closed = once(w, 'closed'); dialog.showMessageBox(w, { message: 'Hello Error' }); w.close(); await closed; @@ -113,7 +115,7 @@ describe('BrowserWindow module', () => { it('closes window without rounded corners', async () => { await closeWindow(w); w = new BrowserWindow({ show: false, frame: false, roundedCorners: false }); - const closed = emittedOnce(w, 'closed'); + const closed = once(w, 'closed'); w.close(); await closed; }); @@ -125,7 +127,7 @@ describe('BrowserWindow module', () => { it('should emit unload handler', async () => { await w.loadFile(path.join(fixtures, 'api', 'unload.html')); - const closed = emittedOnce(w, 'closed'); + const closed = once(w, 'closed'); w.close(); await closed; const test = path.join(fixtures, 'api', 'unload'); @@ -137,12 +139,12 @@ describe('BrowserWindow module', () => { it('should emit beforeunload handler', async () => { await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html')); w.close(); - await emittedOnce(w.webContents, 'before-unload-fired'); + await once(w.webContents, 'before-unload-fired'); }); it('should not crash when keyboard event is sent before closing', async () => { await w.loadURL('data:text/html,pls no crash'); - const closed = emittedOnce(w, 'closed'); + const closed = once(w, 'closed'); w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Escape' }); w.close(); await closed; @@ -199,7 +201,7 @@ describe('BrowserWindow module', () => { w.webContents.once((name as any), () => { w.close(); }); - const destroyed = emittedOnce(w.webContents, 'destroyed'); + const destroyed = once(w.webContents, 'destroyed'); w.webContents.loadURL(url + path); await destroyed; }); @@ -219,7 +221,7 @@ describe('BrowserWindow module', () => { it('should emit unload event', async () => { w.loadFile(path.join(fixtures, 'api', 'close.html')); - await emittedOnce(w, 'closed'); + await once(w, 'closed'); const test = path.join(fixtures, 'api', 'close'); const content = fs.readFileSync(test).toString(); fs.unlinkSync(test); @@ -229,7 +231,7 @@ describe('BrowserWindow module', () => { it('should emit beforeunload event', async function () { await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html')); w.webContents.executeJavaScript('window.close()', true); - await emittedOnce(w.webContents, 'before-unload-fired'); + await once(w.webContents, 'before-unload-fired'); }); }); @@ -336,7 +338,7 @@ describe('BrowserWindow module', () => { res.end(); } } - setTimeout(respond, req.url && req.url.includes('slow') ? 200 : 0); + setTimeout(req.url && req.url.includes('slow') ? 200 : 0).then(respond); }); url = (await listen(server)).url; @@ -347,19 +349,19 @@ describe('BrowserWindow module', () => { }); it('should emit did-start-loading event', async () => { - const didStartLoading = emittedOnce(w.webContents, 'did-start-loading'); + const didStartLoading = once(w.webContents, 'did-start-loading'); w.loadURL('about:blank'); await didStartLoading; }); it('should emit ready-to-show event', async () => { - const readyToShow = emittedOnce(w, 'ready-to-show'); + const readyToShow = once(w, 'ready-to-show'); w.loadURL('about:blank'); await readyToShow; }); // TODO(deepak1556): The error code now seems to be `ERR_FAILED`, verify what // changed and adjust the test. it.skip('should emit did-fail-load event for files that do not exist', async () => { - const didFailLoad = emittedOnce(w.webContents, 'did-fail-load'); + const didFailLoad = once(w.webContents, 'did-fail-load'); w.loadURL('file://a.txt'); const [, code, desc,, isMainFrame] = await didFailLoad; expect(code).to.equal(-6); @@ -367,7 +369,7 @@ describe('BrowserWindow module', () => { expect(isMainFrame).to.equal(true); }); it('should emit did-fail-load event for invalid URL', async () => { - const didFailLoad = emittedOnce(w.webContents, 'did-fail-load'); + const didFailLoad = once(w.webContents, 'did-fail-load'); w.loadURL('http://example:port'); const [, code, desc,, isMainFrame] = await didFailLoad; expect(desc).to.equal('ERR_INVALID_URL'); @@ -375,7 +377,7 @@ describe('BrowserWindow module', () => { expect(isMainFrame).to.equal(true); }); it('should set `mainFrame = false` on did-fail-load events in iframes', async () => { - const didFailLoad = emittedOnce(w.webContents, 'did-fail-load'); + const didFailLoad = once(w.webContents, 'did-fail-load'); w.loadFile(path.join(fixtures, 'api', 'did-fail-load-iframe.html')); const [,,,, isMainFrame] = await didFailLoad; expect(isMainFrame).to.equal(false); @@ -389,7 +391,7 @@ describe('BrowserWindow module', () => { }); it('should emit did-fail-load event for URL exceeding character limit', async () => { const data = Buffer.alloc(2 * 1024 * 1024).toString('base64'); - const didFailLoad = emittedOnce(w.webContents, 'did-fail-load'); + const didFailLoad = once(w.webContents, 'did-fail-load'); w.loadURL(`data:image/png;base64,${data}`); const [, code, desc,, isMainFrame] = await didFailLoad; expect(desc).to.equal('ERR_INVALID_URL'); @@ -498,7 +500,7 @@ describe('BrowserWindow module', () => { }); it('allows the window to be closed from the event listener', async () => { - const event = emittedOnce(w.webContents, 'will-navigate'); + const event = once(w.webContents, 'will-navigate'); w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html')); await event; w.close(); @@ -552,7 +554,7 @@ describe('BrowserWindow module', () => { it('is triggered when a cross-origin iframe navigates _top', async () => { await w.loadURL(`data:text/html,`); - await delay(1000); + await setTimeout(1000); w.webContents.debugger.attach('1.1'); const targets = await w.webContents.debugger.sendCommand('Target.getTargets'); const iframeTarget = targets.targetInfos.find((t: any) => t.type === 'iframe'); @@ -578,7 +580,7 @@ describe('BrowserWindow module', () => { clickCount: 1, button: 'left' }, sessionId); - await emittedOnce(w.webContents, 'did-navigate'); + await once(w.webContents, 'did-navigate'); expect(willNavigateEmitted).to.be.true(); }); }); @@ -605,7 +607,7 @@ describe('BrowserWindow module', () => { server.close(); }); it('is emitted on redirects', async () => { - const willRedirect = emittedOnce(w.webContents, 'will-redirect'); + const willRedirect = once(w.webContents, 'will-redirect'); w.loadURL(`${url}/302`); await willRedirect; }); @@ -615,7 +617,7 @@ describe('BrowserWindow module', () => { w.webContents.on('will-navigate', () => { navigateCalled = true; }); - const willRedirect = emittedOnce(w.webContents, 'will-redirect'); + const willRedirect = once(w.webContents, 'will-redirect'); w.loadURL(`${url}/navigate-302`); await willRedirect; expect(navigateCalled).to.equal(true, 'should have called will-navigate first'); @@ -626,14 +628,14 @@ describe('BrowserWindow module', () => { w.webContents.on('did-stop-loading', () => { stopCalled = true; }); - const willRedirect = emittedOnce(w.webContents, 'will-redirect'); + const willRedirect = once(w.webContents, 'will-redirect'); w.loadURL(`${url}/302`); await willRedirect; expect(stopCalled).to.equal(false, 'should not have called did-stop-loading first'); }); it('allows the window to be closed from the event listener', async () => { - const event = emittedOnce(w.webContents, 'will-redirect'); + const event = once(w.webContents, 'will-redirect'); w.loadURL(`${url}/302`); await event; w.close(); @@ -682,15 +684,19 @@ describe('BrowserWindow module', () => { describe('BrowserWindow.show()', () => { it('should focus on window', async () => { - await emittedOnce(w, 'focus', () => w.show()); + const p = once(w, 'focus'); + w.show(); + await p; expect(w.isFocused()).to.equal(true); }); it('should make the window visible', async () => { - await emittedOnce(w, 'focus', () => w.show()); + const p = once(w, 'focus'); + w.show(); + await p; expect(w.isVisible()).to.equal(true); }); it('emits when window is shown', async () => { - const show = emittedOnce(w, 'show'); + const show = once(w, 'show'); w.show(); await show; expect(w.isVisible()).to.equal(true); @@ -708,10 +714,10 @@ describe('BrowserWindow module', () => { expect(w.isVisible()).to.equal(false); }); it('emits when window is hidden', async () => { - const shown = emittedOnce(w, 'show'); + const shown = once(w, 'show'); w.show(); await shown; - const hidden = emittedOnce(w, 'hide'); + const hidden = once(w, 'hide'); w.hide(); await hidden; expect(w.isVisible()).to.equal(false); @@ -726,14 +732,14 @@ describe('BrowserWindow module', () => { // TODO(dsanders11): Enable for Linux once CI plays nice with these kinds of tests ifit(process.platform !== 'linux')('should not restore maximized windows', async () => { - const maximize = emittedOnce(w, 'maximize'); - const shown = emittedOnce(w, 'show'); + const maximize = once(w, 'maximize'); + const shown = once(w, 'show'); w.maximize(); // TODO(dsanders11): The maximize event isn't firing on macOS for a window initially hidden if (process.platform !== 'darwin') { await maximize; } else { - await delay(1000); + await setTimeout(1000); } w.showInactive(); await shown; @@ -750,8 +756,8 @@ describe('BrowserWindow module', () => { ifit(process.platform !== 'win32')('focuses a blurred window', async () => { { - const isBlurred = emittedOnce(w, 'blur'); - const isShown = emittedOnce(w, 'show'); + const isBlurred = once(w, 'blur'); + const isShown = once(w, 'show'); w.show(); w.blur(); await isShown; @@ -767,10 +773,10 @@ describe('BrowserWindow module', () => { const w2 = new BrowserWindow({ show: false }); const w3 = new BrowserWindow({ show: false }); { - const isFocused3 = emittedOnce(w3, 'focus'); - const isShown1 = emittedOnce(w1, 'show'); - const isShown2 = emittedOnce(w2, 'show'); - const isShown3 = emittedOnce(w3, 'show'); + const isFocused3 = once(w3, 'focus'); + const isShown1 = once(w1, 'show'); + const isShown2 = once(w2, 'show'); + const isShown3 = once(w3, 'show'); w1.show(); w2.show(); w3.show(); @@ -800,9 +806,9 @@ describe('BrowserWindow module', () => { expect(w3.isFocused()).to.equal(true); { - const isClosed1 = emittedOnce(w1, 'closed'); - const isClosed2 = emittedOnce(w2, 'closed'); - const isClosed3 = emittedOnce(w3, 'closed'); + const isClosed1 = once(w1, 'closed'); + const isClosed2 = once(w2, 'closed'); + const isClosed3 = once(w3, 'closed'); w1.destroy(); w2.destroy(); w3.destroy(); @@ -818,8 +824,8 @@ describe('BrowserWindow module', () => { ifdescribe(process.platform !== 'win32')('BrowserWindow.blur()', () => { it('removes focus from window', async () => { { - const isFocused = emittedOnce(w, 'focus'); - const isShown = emittedOnce(w, 'show'); + const isFocused = once(w, 'focus'); + const isShown = once(w, 'show'); w.show(); await isShown; await isFocused; @@ -834,10 +840,10 @@ describe('BrowserWindow module', () => { const w2 = new BrowserWindow({ show: false }); const w3 = new BrowserWindow({ show: false }); { - const isFocused3 = emittedOnce(w3, 'focus'); - const isShown1 = emittedOnce(w1, 'show'); - const isShown2 = emittedOnce(w2, 'show'); - const isShown3 = emittedOnce(w3, 'show'); + const isFocused3 = once(w3, 'focus'); + const isShown1 = once(w1, 'show'); + const isShown2 = once(w2, 'show'); + const isShown3 = once(w3, 'show'); w1.show(); w2.show(); w3.show(); @@ -867,9 +873,9 @@ describe('BrowserWindow module', () => { expect(w3.isFocused()).to.equal(true); { - const isClosed1 = emittedOnce(w1, 'closed'); - const isClosed2 = emittedOnce(w2, 'closed'); - const isClosed3 = emittedOnce(w3, 'closed'); + const isClosed1 = once(w1, 'closed'); + const isClosed2 = once(w2, 'closed'); + const isClosed3 = once(w3, 'closed'); w1.destroy(); w2.destroy(); w3.destroy(); @@ -882,9 +888,11 @@ describe('BrowserWindow module', () => { describe('BrowserWindow.getFocusedWindow()', () => { it('returns the opener window when dev tools window is focused', async () => { - await emittedOnce(w, 'focus', () => w.show()); + const p = once(w, 'focus'); + w.show(); + await p; w.webContents.openDevTools({ mode: 'undocked' }); - await emittedOnce(w.webContents, 'devtools-focused'); + await once(w.webContents, 'devtools-focused'); expect(BrowserWindow.getFocusedWindow()).to.equal(w); }); }); @@ -892,14 +900,14 @@ describe('BrowserWindow module', () => { describe('BrowserWindow.moveTop()', () => { it('should not steal focus', async () => { const posDelta = 50; - const wShownInactive = emittedOnce(w, 'show'); + const wShownInactive = once(w, 'show'); w.showInactive(); await wShownInactive; expect(w.isFocused()).to.equal(false); const otherWindow = new BrowserWindow({ show: false, title: 'otherWindow' }); - const otherWindowShown = emittedOnce(otherWindow, 'show'); - const otherWindowFocused = emittedOnce(otherWindow, 'focus'); + const otherWindowShown = once(otherWindow, 'show'); + const otherWindowFocused = once(otherWindow, 'focus'); otherWindow.show(); await otherWindowShown; await otherWindowFocused; @@ -907,14 +915,14 @@ describe('BrowserWindow module', () => { w.moveTop(); const wPos = w.getPosition(); - const wMoving = emittedOnce(w, 'move'); + const wMoving = once(w, 'move'); w.setPosition(wPos[0] + posDelta, wPos[1] + posDelta); await wMoving; expect(w.isFocused()).to.equal(false); expect(otherWindow.isFocused()).to.equal(true); - const wFocused = emittedOnce(w, 'focus'); - const otherWindowBlurred = emittedOnce(otherWindow, 'blur'); + const wFocused = once(w, 'focus'); + const otherWindowBlurred = once(otherWindow, 'blur'); w.focus(); await wFocused; await otherWindowBlurred; @@ -922,7 +930,7 @@ describe('BrowserWindow module', () => { otherWindow.moveTop(); const otherWindowPos = otherWindow.getPosition(); - const otherWindowMoving = emittedOnce(otherWindow, 'move'); + const otherWindowMoving = once(otherWindow, 'move'); otherWindow.setPosition(otherWindowPos[0] + posDelta, otherWindowPos[1] + posDelta); await otherWindowMoving; expect(otherWindow.isFocused()).to.equal(false); @@ -967,7 +975,7 @@ describe('BrowserWindow module', () => { it('should not throw an exception', async () => { const w2 = new BrowserWindow({ show: false, title: 'window2' }); - const w2Shown = emittedOnce(w2, 'show'); + const w2Shown = once(w2, 'show'); w2.show(); await w2Shown; @@ -982,7 +990,7 @@ describe('BrowserWindow module', () => { describe('BrowserWindow.setFocusable()', () => { it('can set unfocusable window to focusable', async () => { const w2 = new BrowserWindow({ focusable: false }); - const w2Focused = emittedOnce(w2, 'focus'); + const w2Focused = once(w2, 'focus'); w2.setFocusable(true); w2.focus(); await w2Focused; @@ -1038,7 +1046,7 @@ describe('BrowserWindow module', () => { const fullBounds = { x: 440, y: 225, width: 500, height: 400 }; w.setBounds(fullBounds, true); - await expect(emittedOnce(w, 'resized')).to.eventually.be.fulfilled(); + await expect(once(w, 'resized')).to.eventually.be.fulfilled(); }); }); }); @@ -1047,7 +1055,7 @@ describe('BrowserWindow module', () => { it('sets the window size', async () => { const size = [300, 400]; - const resized = emittedOnce(w, 'resize'); + const resized = once(w, 'resize'); w.setSize(size[0], size[1]); await resized; @@ -1059,7 +1067,7 @@ describe('BrowserWindow module', () => { const size = [300, 400]; w.setSize(size[0], size[1], true); - await expect(emittedOnce(w, 'resized')).to.eventually.be.fulfilled(); + await expect(once(w, 'resized')).to.eventually.be.fulfilled(); }); }); }); @@ -1084,7 +1092,7 @@ describe('BrowserWindow module', () => { const size = [300, 400]; w.setAspectRatio(1 / 2); w.setAspectRatio(0); - const resize = emittedOnce(w, 'resize'); + const resize = once(w, 'resize'); w.setSize(size[0], size[1]); await resize; expectBoundsEqual(w.getSize(), size); @@ -1105,7 +1113,7 @@ describe('BrowserWindow module', () => { describe('BrowserWindow.setPosition(x, y)', () => { it('sets the window position', async () => { const pos = [10, 10]; - const move = emittedOnce(w, 'move'); + const move = once(w, 'move'); w.setPosition(pos[0], pos[1]); await move; expect(w.getPosition()).to.deep.equal(pos); @@ -1143,10 +1151,10 @@ describe('BrowserWindow module', () => { describe('BrowserWindow.setContentBounds(bounds)', () => { it('sets the content size and position', async () => { const bounds = { x: 10, y: 10, width: 250, height: 250 }; - const resize = emittedOnce(w, 'resize'); + const resize = once(w, 'resize'); w.setContentBounds(bounds); await resize; - await delay(); + await setTimeout(); expectBoundsEqual(w.getContentBounds(), bounds); }); it('works for a frameless window', async () => { @@ -1158,10 +1166,10 @@ describe('BrowserWindow module', () => { height: 300 }); const bounds = { x: 10, y: 10, width: 250, height: 250 }; - const resize = emittedOnce(w, 'resize'); + const resize = once(w, 'resize'); w.setContentBounds(bounds); await resize; - await delay(); + await setTimeout(); expectBoundsEqual(w.getContentBounds(), bounds); }); }); @@ -1212,7 +1220,7 @@ describe('BrowserWindow module', () => { describe('Normal state', () => { it('checks normal bounds after resize', async () => { const size = [300, 400]; - const resize = emittedOnce(w, 'resize'); + const resize = once(w, 'resize'); w.setSize(size[0], size[1]); await resize; expectBoundsEqual(w.getNormalBounds(), w.getBounds()); @@ -1220,7 +1228,7 @@ describe('BrowserWindow module', () => { it('checks normal bounds after move', async () => { const pos = [10, 10]; - const move = emittedOnce(w, 'move'); + const move = once(w, 'move'); w.setPosition(pos[0], pos[1]); await move; expectBoundsEqual(w.getNormalBounds(), w.getBounds()); @@ -1230,7 +1238,7 @@ describe('BrowserWindow module', () => { ifdescribe(process.platform !== 'linux')('Maximized state', () => { it('checks normal bounds when maximized', async () => { const bounds = w.getBounds(); - const maximize = emittedOnce(w, 'maximize'); + const maximize = once(w, 'maximize'); w.show(); w.maximize(); await maximize; @@ -1239,12 +1247,12 @@ describe('BrowserWindow module', () => { it('updates normal bounds after resize and maximize', async () => { const size = [300, 400]; - const resize = emittedOnce(w, 'resize'); + const resize = once(w, 'resize'); w.setSize(size[0], size[1]); await resize; const original = w.getBounds(); - const maximize = emittedOnce(w, 'maximize'); + const maximize = once(w, 'maximize'); w.maximize(); await maximize; @@ -1254,19 +1262,19 @@ describe('BrowserWindow module', () => { expect(normal).to.deep.equal(original); expect(normal).to.not.deep.equal(bounds); - const close = emittedOnce(w, 'close'); + const close = once(w, 'close'); w.close(); await close; }); it('updates normal bounds after move and maximize', async () => { const pos = [10, 10]; - const move = emittedOnce(w, 'move'); + const move = once(w, 'move'); w.setPosition(pos[0], pos[1]); await move; const original = w.getBounds(); - const maximize = emittedOnce(w, 'maximize'); + const maximize = once(w, 'maximize'); w.maximize(); await maximize; @@ -1276,7 +1284,7 @@ describe('BrowserWindow module', () => { expect(normal).to.deep.equal(original); expect(normal).to.not.deep.equal(bounds); - const close = emittedOnce(w, 'close'); + const close = once(w, 'close'); w.close(); await close; }); @@ -1286,7 +1294,7 @@ describe('BrowserWindow module', () => { w.once('maximize', () => { w.unmaximize(); }); - const unmaximize = emittedOnce(w, 'unmaximize'); + const unmaximize = once(w, 'unmaximize'); w.show(); w.maximize(); await unmaximize; @@ -1307,7 +1315,7 @@ describe('BrowserWindow module', () => { w.once('maximize', () => { w.unmaximize(); }); - const unmaximize = emittedOnce(w, 'unmaximize'); + const unmaximize = once(w, 'unmaximize'); w.show(); w.maximize(); await unmaximize; @@ -1323,12 +1331,12 @@ describe('BrowserWindow module', () => { transparent: true }); - const maximize = emittedOnce(w, 'maximize'); + const maximize = once(w, 'maximize'); w.show(); w.maximize(); await maximize; expect(w.isMaximized()).to.equal(true); - const unmaximize = emittedOnce(w, 'unmaximize'); + const unmaximize = once(w, 'unmaximize'); w.unmaximize(); await unmaximize; expect(w.isMaximized()).to.equal(false); @@ -1343,7 +1351,7 @@ describe('BrowserWindow module', () => { w.setAspectRatio(16 / 11); - const maximize = emittedOnce(w, 'maximize'); + const maximize = once(w, 'maximize'); w.show(); w.maximize(); await maximize; @@ -1357,7 +1365,7 @@ describe('BrowserWindow module', () => { ifdescribe(process.platform !== 'linux')('Minimized state', () => { it('checks normal bounds when minimized', async () => { const bounds = w.getBounds(); - const minimize = emittedOnce(w, 'minimize'); + const minimize = once(w, 'minimize'); w.show(); w.minimize(); await minimize; @@ -1366,12 +1374,12 @@ describe('BrowserWindow module', () => { it('updates normal bounds after move and minimize', async () => { const pos = [10, 10]; - const move = emittedOnce(w, 'move'); + const move = once(w, 'move'); w.setPosition(pos[0], pos[1]); await move; const original = w.getBounds(); - const minimize = emittedOnce(w, 'minimize'); + const minimize = once(w, 'minimize'); w.minimize(); await minimize; @@ -1383,12 +1391,12 @@ describe('BrowserWindow module', () => { it('updates normal bounds after resize and minimize', async () => { const size = [300, 400]; - const resize = emittedOnce(w, 'resize'); + const resize = once(w, 'resize'); w.setSize(size[0], size[1]); await resize; const original = w.getBounds(); - const minimize = emittedOnce(w, 'minimize'); + const minimize = once(w, 'minimize'); w.minimize(); await minimize; @@ -1403,7 +1411,7 @@ describe('BrowserWindow module', () => { w.once('minimize', () => { w.restore(); }); - const restore = emittedOnce(w, 'restore'); + const restore = once(w, 'restore'); w.show(); w.minimize(); await restore; @@ -1424,7 +1432,7 @@ describe('BrowserWindow module', () => { w.once('minimize', () => { w.restore(); }); - const restore = emittedOnce(w, 'restore'); + const restore = once(w, 'restore'); w.show(); w.minimize(); await restore; @@ -1453,7 +1461,7 @@ describe('BrowserWindow module', () => { it('checks normal bounds when fullscreen\'ed', async () => { const bounds = w.getBounds(); - const enterFullScreen = emittedOnce(w, 'enter-full-screen'); + const enterFullScreen = once(w, 'enter-full-screen'); w.show(); w.fullScreen = true; await enterFullScreen; @@ -1462,12 +1470,12 @@ describe('BrowserWindow module', () => { it('updates normal bounds after resize and fullscreen', async () => { const size = [300, 400]; - const resize = emittedOnce(w, 'resize'); + const resize = once(w, 'resize'); w.setSize(size[0], size[1]); await resize; const original = w.getBounds(); - const fsc = emittedOnce(w, 'enter-full-screen'); + const fsc = once(w, 'enter-full-screen'); w.fullScreen = true; await fsc; @@ -1477,19 +1485,19 @@ describe('BrowserWindow module', () => { expect(normal).to.deep.equal(original); expect(normal).to.not.deep.equal(bounds); - const close = emittedOnce(w, 'close'); + const close = once(w, 'close'); w.close(); await close; }); it('updates normal bounds after move and fullscreen', async () => { const pos = [10, 10]; - const move = emittedOnce(w, 'move'); + const move = once(w, 'move'); w.setPosition(pos[0], pos[1]); await move; const original = w.getBounds(); - const fsc = emittedOnce(w, 'enter-full-screen'); + const fsc = once(w, 'enter-full-screen'); w.fullScreen = true; await fsc; @@ -1499,7 +1507,7 @@ describe('BrowserWindow module', () => { expect(normal).to.deep.equal(original); expect(normal).to.not.deep.equal(bounds); - const close = emittedOnce(w, 'close'); + const close = once(w, 'close'); w.close(); await close; }); @@ -1509,7 +1517,7 @@ describe('BrowserWindow module', () => { w.once('enter-full-screen', () => { w.fullScreen = false; }); - const leaveFullScreen = emittedOnce(w, 'leave-full-screen'); + const leaveFullScreen = once(w, 'leave-full-screen'); w.show(); w.fullScreen = true; await leaveFullScreen; @@ -1534,7 +1542,7 @@ describe('BrowserWindow module', () => { const bounds = w.getBounds(); w.show(); - const enterFullScreen = emittedOnce(w, 'enter-full-screen'); + const enterFullScreen = once(w, 'enter-full-screen'); w.setFullScreen(true); await enterFullScreen; @@ -1543,12 +1551,12 @@ describe('BrowserWindow module', () => { it('updates normal bounds after resize and fullscreen', async () => { const size = [300, 400]; - const resize = emittedOnce(w, 'resize'); + const resize = once(w, 'resize'); w.setSize(size[0], size[1]); await resize; const original = w.getBounds(); - const fsc = emittedOnce(w, 'enter-full-screen'); + const fsc = once(w, 'enter-full-screen'); w.setFullScreen(true); await fsc; @@ -1558,19 +1566,19 @@ describe('BrowserWindow module', () => { expect(normal).to.deep.equal(original); expect(normal).to.not.deep.equal(bounds); - const close = emittedOnce(w, 'close'); + const close = once(w, 'close'); w.close(); await close; }); it('updates normal bounds after move and fullscreen', async () => { const pos = [10, 10]; - const move = emittedOnce(w, 'move'); + const move = once(w, 'move'); w.setPosition(pos[0], pos[1]); await move; const original = w.getBounds(); - const fsc = emittedOnce(w, 'enter-full-screen'); + const fsc = once(w, 'enter-full-screen'); w.setFullScreen(true); await fsc; @@ -1580,7 +1588,7 @@ describe('BrowserWindow module', () => { expect(normal).to.deep.equal(original); expect(normal).to.not.deep.equal(bounds); - const close = emittedOnce(w, 'close'); + const close = once(w, 'close'); w.close(); await close; }); @@ -1589,11 +1597,11 @@ describe('BrowserWindow module', () => { const bounds = w.getBounds(); w.show(); - const enterFullScreen = emittedOnce(w, 'enter-full-screen'); + const enterFullScreen = once(w, 'enter-full-screen'); w.setFullScreen(true); await enterFullScreen; - const leaveFullScreen = emittedOnce(w, 'leave-full-screen'); + const leaveFullScreen = once(w, 'leave-full-screen'); w.setFullScreen(false); await leaveFullScreen; @@ -1737,7 +1745,7 @@ describe('BrowserWindow module', () => { w.loadFile(path.join(fixtures, 'pages', 'visibilitychange.html')); { - const [, visibilityState, hidden] = await emittedOnce(ipcMain, 'pong'); + const [, visibilityState, hidden] = await once(ipcMain, 'pong'); expect(visibilityState).to.equal('visible'); expect(hidden).to.be.false('hidden'); } @@ -1745,7 +1753,7 @@ describe('BrowserWindow module', () => { w.hide(); { - const [, visibilityState, hidden] = await emittedOnce(ipcMain, 'pong'); + const [, visibilityState, hidden] = await once(ipcMain, 'pong'); expect(visibilityState).to.equal('hidden'); expect(hidden).to.be.true('hidden'); } @@ -1759,7 +1767,7 @@ describe('BrowserWindow module', () => { it('resolves after the window is hidden', async () => { const w = new BrowserWindow({ show: false }); w.loadFile(path.join(fixtures, 'pages', 'a.html')); - await emittedOnce(w, 'ready-to-show'); + await once(w, 'ready-to-show'); w.show(); const visibleImage = await w.capturePage(); @@ -1776,7 +1784,7 @@ describe('BrowserWindow module', () => { const w = new BrowserWindow({ show: false }); w.webContents.setBackgroundThrottling(false); w.loadFile(path.join(fixtures, 'pages', 'a.html')); - await emittedOnce(w, 'ready-to-show'); + await once(w, 'ready-to-show'); const image = await w.capturePage(); expect(image.isEmpty()).to.equal(false); @@ -1785,7 +1793,7 @@ describe('BrowserWindow module', () => { it('preserves transparency', async () => { const w = new BrowserWindow({ show: false, transparent: true }); w.loadFile(path.join(fixtures, 'pages', 'theme-color.html')); - await emittedOnce(w, 'ready-to-show'); + await once(w, 'ready-to-show'); w.show(); const image = await w.capturePage(); @@ -1866,7 +1874,7 @@ describe('BrowserWindow module', () => { }); it('causes the right value to be emitted on `always-on-top-changed`', async () => { - const alwaysOnTopChanged = emittedOnce(w, 'always-on-top-changed'); + const alwaysOnTopChanged = once(w, 'always-on-top-changed'); expect(w.isAlwaysOnTop()).to.be.false('is alwaysOnTop'); w.setAlwaysOnTop(true); const [, alwaysOnTop] = await alwaysOnTopChanged; @@ -1928,7 +1936,7 @@ describe('BrowserWindow module', () => { it('parses ', async () => { w = new BrowserWindow({ show: true }); - const p = emittedOnce(w.webContents.session, 'preconnect'); + const p = once(w.webContents.session, 'preconnect'); w.loadURL(url + '/link'); const [, preconnectUrl, allowCredentials] = await p; expect(preconnectUrl).to.equal('http://example.com/'); @@ -2016,11 +2024,11 @@ describe('BrowserWindow module', () => { w.setWindowButtonVisibility(false); expect(w._getWindowButtonVisibility()).to.equal(false); - const enterFS = emittedOnce(w, 'enter-full-screen'); + const enterFS = once(w, 'enter-full-screen'); w.setFullScreen(true); await enterFS; - const leaveFS = emittedOnce(w, 'leave-full-screen'); + const leaveFS = once(w, 'leave-full-screen'); w.setFullScreen(false); await leaveFS; @@ -2034,11 +2042,11 @@ describe('BrowserWindow module', () => { w.setWindowButtonVisibility(false); expect(w._getWindowButtonVisibility()).to.equal(false); - const enterFS = emittedOnce(w, 'enter-full-screen'); + const enterFS = once(w, 'enter-full-screen'); w.setFullScreen(true); await enterFS; - const leaveFS = emittedOnce(w, 'leave-full-screen'); + const leaveFS = once(w, 'leave-full-screen'); w.setFullScreen(false); await leaveFS; @@ -2206,7 +2214,7 @@ describe('BrowserWindow module', () => { appProcess = childProcess.spawn(process.execPath, [appPath]); - const [code] = await emittedOnce(appProcess, 'exit'); + const [code] = await once(appProcess, 'exit'); expect(code).to.equal(0); }); }); @@ -2247,8 +2255,8 @@ describe('BrowserWindow module', () => { // NOTE(nornagon): Waiting for 'did-attach-webview' is a workaround for // https://github.com/electron/electron/issues/25413, and is not integral // to the test. - const p = emittedOnce(w.webContents, 'did-attach-webview'); - const [, webviewContents] = await emittedOnce(app, 'web-contents-created'); + const p = once(w.webContents, 'did-attach-webview'); + const [, webviewContents] = await once(app, 'web-contents-created'); expect(BrowserWindow.fromWebContents(webviewContents)!.id).to.equal(w.id); await p; }); @@ -2257,7 +2265,7 @@ describe('BrowserWindow module', () => { const w = new BrowserWindow({ show: false }); w.loadURL('about:blank'); w.webContents.executeJavaScript('window.open(""); null'); - const [win, winFromWebContents] = await new Promise((resolve) => { + const [win, winFromWebContents] = await new Promise((resolve) => { app.once('browser-window-created', (e, win) => { resolve([win, BrowserWindow.fromWebContents(win.webContents)]); }); @@ -2419,7 +2427,7 @@ describe('BrowserWindow module', () => { if (process.platform === 'darwin') { await w.loadFile(overlayHTML); } else { - const overlayReady = emittedOnce(ipcMain, 'geometrychange'); + const overlayReady = once(ipcMain, 'geometrychange'); await w.loadFile(overlayHTML); await overlayReady; } @@ -2436,7 +2444,7 @@ describe('BrowserWindow module', () => { expect(overlayRect.height).to.be.greaterThan(0); const cssOverlayRect = await w.webContents.executeJavaScript('getCssOverlayProperties();'); expect(cssOverlayRect).to.deep.equal(overlayRect); - const geometryChange = emittedOnce(ipcMain, 'geometrychange'); + const geometryChange = once(ipcMain, 'geometrychange'); w.setBounds({ width: 800 }); const [, newOverlayRect] = await geometryChange; expect(newOverlayRect.width).to.equal(overlayRect.width + 400); @@ -2527,7 +2535,7 @@ describe('BrowserWindow module', () => { if (process.platform === 'darwin') { await w.loadFile(overlayHTML); } else { - const overlayReady = emittedOnce(ipcMain, 'geometrychange'); + const overlayReady = once(ipcMain, 'geometrychange'); await w.loadFile(overlayHTML); await overlayReady; } @@ -2536,7 +2544,7 @@ describe('BrowserWindow module', () => { const overlayRectPreMax = await w.webContents.executeJavaScript('getJSOverlayProperties()'); if (!w.isMaximized()) { - const maximize = emittedOnce(w, 'maximize'); + const maximize = once(w, 'maximize'); w.show(); w.maximize(); await maximize; @@ -2596,7 +2604,7 @@ describe('BrowserWindow module', () => { it('correctly updates the height of the overlay', async () => { const testOverlay = async (w: BrowserWindow, size: Number, firstRun: boolean) => { const overlayHTML = path.join(__dirname, 'fixtures', 'pages', 'overlay.html'); - const overlayReady = emittedOnce(ipcMain, 'geometrychange'); + const overlayReady = once(ipcMain, 'geometrychange'); await w.loadFile(overlayHTML); if (firstRun) { await overlayReady; @@ -2607,7 +2615,7 @@ describe('BrowserWindow module', () => { const { height: preMaxHeight } = await w.webContents.executeJavaScript('getJSOverlayProperties()'); if (!w.isMaximized()) { - const maximize = emittedOnce(w, 'maximize'); + const maximize = once(w, 'maximize'); w.show(); w.maximize(); await maximize; @@ -2738,7 +2746,7 @@ describe('BrowserWindow module', () => { show: false }); w.loadFile(path.join(fixtures, 'api', 'no-leak.html')); - const [, result] = await emittedOnce(ipcMain, 'leak-result'); + const [, result] = await once(ipcMain, 'leak-result'); expect(result).to.have.property('require', 'undefined'); expect(result).to.have.property('exports', 'undefined'); expect(result).to.have.property('windowExports', 'undefined'); @@ -2776,7 +2784,7 @@ describe('BrowserWindow module', () => { show: false }); w.loadFile(path.join(fixtures, 'api', 'globals.html')); - const [, notIsolated] = await emittedOnce(ipcMain, 'leak-result'); + const [, notIsolated] = await once(ipcMain, 'leak-result'); expect(notIsolated).to.have.property('globals'); w.destroy(); @@ -2789,7 +2797,7 @@ describe('BrowserWindow module', () => { show: false }); w.loadFile(path.join(fixtures, 'api', 'globals.html')); - const [, isolated] = await emittedOnce(ipcMain, 'leak-result'); + const [, isolated] = await once(ipcMain, 'leak-result'); expect(isolated).to.have.property('globals'); const notIsolatedGlobals = new Set(notIsolated.globals); for (const isolatedGlobal of isolated.globals) { @@ -2809,7 +2817,7 @@ describe('BrowserWindow module', () => { } }); w.loadFile(path.join(fixtures, 'api', 'preload.html')); - const [, test] = await emittedOnce(ipcMain, 'answer'); + const [, test] = await once(ipcMain, 'answer'); expect(test).to.eql('preload'); }); it('has synchronous access to all eventual window APIs', async () => { @@ -2823,7 +2831,7 @@ describe('BrowserWindow module', () => { } }); w.loadFile(path.join(fixtures, 'api', 'preload.html')); - const [, test] = await emittedOnce(ipcMain, 'answer'); + const [, test] = await once(ipcMain, 'answer'); expect(test).to.be.an('object'); expect(test.atPreload).to.be.an('array'); expect(test.atLoad).to.be.an('array'); @@ -2863,7 +2871,7 @@ describe('BrowserWindow module', () => { } }); w.loadURL('about:blank'); - const [, preload1, preload2, preload3] = await emittedOnce(ipcMain, 'vars'); + const [, preload1, preload2, preload3] = await once(ipcMain, 'vars'); expect(preload1).to.equal('preload-1'); expect(preload2).to.equal('preload-1-2'); expect(preload3).to.be.undefined('preload 3'); @@ -2887,7 +2895,7 @@ describe('BrowserWindow module', () => { } }); w.loadFile(path.join(fixtures, 'api', 'blank.html')); - const [, argv] = await emittedOnce(ipcMain, 'answer'); + const [, argv] = await once(ipcMain, 'answer'); expect(argv).to.include('--my-magic-arg'); }); @@ -2902,7 +2910,7 @@ describe('BrowserWindow module', () => { } }); w.loadFile(path.join(fixtures, 'api', 'blank.html')); - const [, argv] = await emittedOnce(ipcMain, 'answer'); + const [, argv] = await once(ipcMain, 'answer'); expect(argv).to.include('--my-magic-arg=foo'); }); }); @@ -2918,7 +2926,7 @@ describe('BrowserWindow module', () => { } }); w.loadFile(path.join(fixtures, 'api', 'blank.html')); - const [, typeofProcess, typeofBuffer] = await emittedOnce(ipcMain, 'answer'); + const [, typeofProcess, typeofBuffer] = await once(ipcMain, 'answer'); expect(typeofProcess).to.equal('undefined'); expect(typeofBuffer).to.equal('undefined'); }); @@ -2957,7 +2965,7 @@ describe('BrowserWindow module', () => { } }); w.loadFile(path.join(fixtures, 'api', 'preload.html')); - const [, test] = await emittedOnce(ipcMain, 'answer'); + const [, test] = await once(ipcMain, 'answer'); expect(test).to.equal('preload'); }); @@ -2972,7 +2980,7 @@ describe('BrowserWindow module', () => { } }); w.loadFile(path.join(fixtures, 'api', 'preload.html')); - const [, test] = await emittedOnce(ipcMain, 'answer'); + const [, test] = await once(ipcMain, 'answer'); expect(test).to.equal('preload'); }); @@ -2985,7 +2993,7 @@ describe('BrowserWindow module', () => { } }); w.loadURL('about:blank'); - await emittedOnce(ipcMain, 'process-loaded'); + await once(ipcMain, 'process-loaded'); }); it('exposes "exit" event to preload script', async () => { @@ -3000,7 +3008,7 @@ describe('BrowserWindow module', () => { const htmlPath = path.join(__dirname, 'fixtures', 'api', 'sandbox.html?exit-event'); const pageUrl = 'file://' + htmlPath; w.loadURL(pageUrl); - const [, url] = await emittedOnce(ipcMain, 'answer'); + const [, url] = await once(ipcMain, 'answer'); const expectedUrl = process.platform === 'win32' ? 'file:///' + htmlPath.replace(/\\/g, '/') : pageUrl; @@ -3016,7 +3024,7 @@ describe('BrowserWindow module', () => { } }); w.loadURL('about:blank'); - const [, rendererEventEmitterProperties] = await emittedOnce(ipcMain, 'answer'); + const [, rendererEventEmitterProperties] = await once(ipcMain, 'answer'); const { EventEmitter } = require('events'); const emitter = new EventEmitter(); const browserEventEmitterProperties = []; @@ -3048,9 +3056,9 @@ describe('BrowserWindow module', () => { const htmlPath = path.join(__dirname, 'fixtures', 'api', 'sandbox.html?window-open'); const pageUrl = 'file://' + htmlPath; - const answer = emittedOnce(ipcMain, 'answer'); + const answer = once(ipcMain, 'answer'); w.loadURL(pageUrl); - const [, { url, frameName, options }] = await emittedOnce(w.webContents, 'did-create-window'); + const [, { url, frameName, options }] = await once(w.webContents, 'did-create-window'); const expectedUrl = process.platform === 'win32' ? 'file:///' + htmlPath.replace(/\\/g, '/') : pageUrl; @@ -3087,7 +3095,7 @@ describe('BrowserWindow module', () => { ); // Wait for a message from the main window saying that it's ready. - await emittedOnce(ipcMain, 'opener-loaded'); + await once(ipcMain, 'opener-loaded'); // Ask the opener to open a popup with window.opener. const expectedPopupUrl = `${serverUrl}/cross-site`; // Set in "sandbox.html". @@ -3096,22 +3104,22 @@ describe('BrowserWindow module', () => { // The page is going to open a popup that it won't be able to close. // We have to close it from here later. - const [, popupWindow] = await emittedOnce(app, 'browser-window-created'); + const [, popupWindow] = await once(app, 'browser-window-created'); // Ask the popup window for details. - const detailsAnswer = emittedOnce(ipcMain, 'child-loaded'); + const detailsAnswer = once(ipcMain, 'child-loaded'); popupWindow.webContents.send('provide-details'); const [, openerIsNull, , locationHref] = await detailsAnswer; expect(openerIsNull).to.be.false('window.opener is null'); expect(locationHref).to.equal(expectedPopupUrl); // Ask the page to access the popup. - const touchPopupResult = emittedOnce(ipcMain, 'answer'); + const touchPopupResult = once(ipcMain, 'answer'); w.webContents.send('touch-the-popup'); const [, popupAccessMessage] = await touchPopupResult; // Ask the popup to access the opener. - const touchOpenerResult = emittedOnce(ipcMain, 'answer'); + const touchOpenerResult = once(ipcMain, 'answer'); popupWindow.webContents.send('touch-the-opener'); const [, openerAccessMessage] = await touchOpenerResult; @@ -3138,7 +3146,7 @@ describe('BrowserWindow module', () => { const preloadPath = path.join(mainFixtures, 'api', 'new-window-preload.js'); w.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { preload: preloadPath } } })); w.loadFile(path.join(fixtures, 'api', 'new-window.html')); - const [, { argv }] = await emittedOnce(ipcMain, 'answer'); + const [, { argv }] = await once(ipcMain, 'answer'); expect(argv).to.include('--enable-sandbox'); }); @@ -3154,8 +3162,8 @@ describe('BrowserWindow module', () => { w.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { preload: preloadPath, contextIsolation: false } } })); w.loadFile(path.join(fixtures, 'api', 'new-window.html')); const [[, childWebContents]] = await Promise.all([ - emittedOnce(app, 'web-contents-created'), - emittedOnce(ipcMain, 'answer') + once(app, 'web-contents-created'), + once(ipcMain, 'answer') ]); const webPreferences = childWebContents.getLastWebPreferences(); expect(webPreferences.contextIsolation).to.equal(false); @@ -3191,7 +3199,7 @@ describe('BrowserWindow module', () => { const done = Promise.all([ 'parent-answer', 'child-answer' - ].map(name => emittedOnce(ipcMain, name))); + ].map(name => once(ipcMain, name))); w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'verify-ipc-sender' }); await done; }); @@ -3202,7 +3210,7 @@ describe('BrowserWindow module', () => { w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }); }); it('works for window events', async () => { - const pageTitleUpdated = emittedOnce(w, 'page-title-updated'); + const pageTitleUpdated = once(w, 'page-title-updated'); w.loadURL('data:text/html,'); await pageTitleUpdated; }); @@ -3212,7 +3220,7 @@ describe('BrowserWindow module', () => { 'did-navigate', 'did-fail-load', 'did-stop-loading' - ].map(name => emittedOnce(w.webContents, name))); + ].map(name => once(w.webContents, name))); w.loadURL('data:text/html,'); await done; }); @@ -3227,7 +3235,7 @@ describe('BrowserWindow module', () => { 'did-stop-loading', 'did-frame-finish-load', 'dom-ready' - ].map(name => emittedOnce(w.webContents, name))); + ].map(name => once(w.webContents, name))); w.loadFile(path.join(__dirname, 'fixtures', 'api', 'sandbox.html'), { search: 'webcontents-events' }); await done; }); @@ -3247,7 +3255,7 @@ describe('BrowserWindow module', () => { }); process.env.sandboxmain = 'foo'; w.loadFile(path.join(fixtures, 'api', 'preload.html')); - const [, test] = await emittedOnce(ipcMain, 'answer'); + const [, test] = await once(ipcMain, 'answer'); expect(test.hasCrash).to.be.true('has crash'); expect(test.hasHang).to.be.true('has hang'); expect(test.heapStatistics).to.be.an('object'); @@ -3287,8 +3295,8 @@ describe('BrowserWindow module', () => { contextIsolation: false } }); - const didAttachWebview = emittedOnce(w.webContents, 'did-attach-webview'); - const webviewDomReady = emittedOnce(ipcMain, 'webview-dom-ready'); + const didAttachWebview = once(w.webContents, 'did-attach-webview'); + const webviewDomReady = once(ipcMain, 'webview-dom-ready'); w.loadFile(path.join(fixtures, 'pages', 'webview-did-attach-event.html')); const [, webContents] = await didAttachWebview; @@ -3313,25 +3321,25 @@ describe('BrowserWindow module', () => { }); it('opens window of about:blank with cross-scripting enabled', async () => { - const answer = emittedOnce(ipcMain, 'answer'); + const answer = once(ipcMain, 'answer'); w.loadFile(path.join(fixtures, 'api', 'native-window-open-blank.html')); const [, content] = await answer; expect(content).to.equal('Hello'); }); it('opens window of same domain with cross-scripting enabled', async () => { - const answer = emittedOnce(ipcMain, 'answer'); + const answer = once(ipcMain, 'answer'); w.loadFile(path.join(fixtures, 'api', 'native-window-open-file.html')); const [, content] = await answer; expect(content).to.equal('Hello'); }); it('blocks accessing cross-origin frames', async () => { - const answer = emittedOnce(ipcMain, 'answer'); + const answer = once(ipcMain, 'answer'); w.loadFile(path.join(fixtures, 'api', 'native-window-open-cross-origin.html')); const [, content] = await answer; expect(content).to.equal('Blocked a frame with origin "file://" from accessing a cross-origin frame.'); }); it('opens window from `); w.webContents.mainFrame.frames[0].executeJavaScript('ipc.send(\'test\', 42)'); - const [, arg] = await emittedOnce(w.webContents.ipc, 'test'); + const [, arg] = await once(w.webContents.ipc, 'test'); expect(arg).to.equal(42); }); }); @@ -662,7 +661,7 @@ describe('ipc module', () => { const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); w.loadURL('about:blank'); w.webContents.executeJavaScript('require(\'electron\').ipcRenderer.send(\'test\', 42)'); - const [, arg] = await emittedOnce(w.webContents.mainFrame.ipc, 'test'); + const [, arg] = await once(w.webContents.mainFrame.ipc, 'test'); expect(arg).to.equal(42); }); @@ -680,7 +679,7 @@ describe('ipc module', () => { const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); w.loadURL('about:blank'); w.webContents.executeJavaScript('require(\'electron\').ipcRenderer.postMessage(\'test\', null, [(new MessageChannel).port1])'); - const [event] = await emittedOnce(w.webContents.mainFrame.ipc, 'test'); + const [event] = await once(w.webContents.mainFrame.ipc, 'test'); expect(event.ports.length).to.equal(1); }); @@ -752,7 +751,7 @@ describe('ipc module', () => { await w.loadURL(`data:text/html,`); w.webContents.mainFrame.frames[0].executeJavaScript('ipc.send(\'test\', 42)'); w.webContents.mainFrame.ipc.on('test', () => { throw new Error('should not be called'); }); - const [, arg] = await emittedOnce(w.webContents.mainFrame.frames[0].ipc, 'test'); + const [, arg] = await once(w.webContents.mainFrame.frames[0].ipc, 'test'); expect(arg).to.equal(42); }); }); diff --git a/spec/api-menu-spec.ts b/spec/api-menu-spec.ts index 5c47ee01f75a..adf10663d69b 100644 --- a/spec/api-menu-spec.ts +++ b/spec/api-menu-spec.ts @@ -3,9 +3,10 @@ import * as path from 'path'; import { expect } from 'chai'; import { BrowserWindow, Menu, MenuItem } from 'electron/main'; import { sortMenuItems } from '../lib/browser/api/menu-utils'; -import { emittedOnce } from './lib/events-helpers'; -import { ifit, delay } from './lib/spec-helpers'; +import { ifit } from './lib/spec-helpers'; import { closeWindow } from './lib/window-helpers'; +import { once } from 'events'; +import { setTimeout } from 'timers/promises'; const fixturesPath = path.resolve(__dirname, 'fixtures'); @@ -817,7 +818,7 @@ describe('Menu module', function () { menu.on('menu-will-close', () => { done(); }); menu.popup({ window: w }); // https://github.com/electron/electron/issues/19411 - setTimeout(() => { + setTimeout().then(() => { menu.closePopup(); }); }); @@ -849,7 +850,7 @@ describe('Menu module', function () { expect(x).to.equal(100); expect(y).to.equal(101); // https://github.com/electron/electron/issues/19411 - setTimeout(() => { + setTimeout().then(() => { menu.closePopup(); }); }); @@ -857,7 +858,7 @@ describe('Menu module', function () { it('works with a given BrowserWindow, no options, and a callback', (done) => { menu.popup({ window: w, callback: () => done() }); // https://github.com/electron/electron/issues/19411 - setTimeout(() => { + setTimeout().then(() => { menu.closePopup(); }); }); @@ -870,14 +871,14 @@ describe('Menu module', function () { // eslint-disable-next-line no-undef const wr = new WeakRef(menu); - await delay(); + await setTimeout(); // Do garbage collection, since |menu| is not referenced in this closure // it would be gone after next call. const v8Util = process._linkedBinding('electron_common_v8_util'); v8Util.requestGarbageCollectionForTesting(); - await delay(); + await setTimeout(); // Try to receive menu from weak reference. if (wr.deref()) { @@ -929,7 +930,7 @@ describe('Menu module', function () { appProcess.stdout.on('data', data => { output += data; }); appProcess.stderr.on('data', data => { output += data; }); - const [code] = await emittedOnce(appProcess, 'exit'); + const [code] = await once(appProcess, 'exit'); if (!output.includes('Window has no menu')) { console.log(code, output); } diff --git a/spec/api-native-theme-spec.ts b/spec/api-native-theme-spec.ts index 265279124f72..c111e9b4b7aa 100644 --- a/spec/api-native-theme-spec.ts +++ b/spec/api-native-theme-spec.ts @@ -1,11 +1,12 @@ import { expect } from 'chai'; import { nativeTheme, systemPreferences, BrowserWindow, ipcMain } from 'electron/main'; +import { once } from 'events'; import * as os from 'os'; import * as path from 'path'; import * as semver from 'semver'; +import { setTimeout } from 'timers/promises'; -import { delay, ifdescribe } from './lib/spec-helpers'; -import { emittedOnce } from './lib/events-helpers'; +import { ifdescribe } from './lib/spec-helpers'; import { closeAllWindows } from './lib/window-helpers'; describe('nativeTheme module', () => { @@ -19,7 +20,7 @@ describe('nativeTheme module', () => { afterEach(async () => { nativeTheme.themeSource = 'system'; // Wait for any pending events to emit - await delay(20); + await setTimeout(20); closeAllWindows(); }); @@ -37,10 +38,10 @@ describe('nativeTheme module', () => { it('should emit the "updated" event when it is set and the resulting "shouldUseDarkColors" value changes', async () => { nativeTheme.themeSource = 'light'; - let updatedEmitted = emittedOnce(nativeTheme, 'updated'); + let updatedEmitted = once(nativeTheme, 'updated'); nativeTheme.themeSource = 'dark'; await updatedEmitted; - updatedEmitted = emittedOnce(nativeTheme, 'updated'); + updatedEmitted = once(nativeTheme, 'updated'); nativeTheme.themeSource = 'light'; await updatedEmitted; }); @@ -48,14 +49,14 @@ describe('nativeTheme module', () => { it('should not emit the "updated" event when it is set and the resulting "shouldUseDarkColors" value is the same', async () => { nativeTheme.themeSource = 'dark'; // Wait a few ticks to allow an async events to flush - await delay(20); + await setTimeout(20); let called = false; nativeTheme.once('updated', () => { called = true; }); nativeTheme.themeSource = 'dark'; // Wait a few ticks to allow an async events to flush - await delay(20); + await setTimeout(20); expect(called).to.equal(false); }); @@ -83,15 +84,15 @@ describe('nativeTheme module', () => { .addEventListener('change', () => require('electron').ipcRenderer.send('theme-change')) `); const originalSystemIsDark = await getPrefersColorSchemeIsDark(w); - let changePromise: Promise = emittedOnce(ipcMain, 'theme-change'); + let changePromise: Promise = once(ipcMain, 'theme-change'); nativeTheme.themeSource = 'dark'; if (!originalSystemIsDark) await changePromise; expect(await getPrefersColorSchemeIsDark(w)).to.equal(true); - changePromise = emittedOnce(ipcMain, 'theme-change'); + changePromise = once(ipcMain, 'theme-change'); nativeTheme.themeSource = 'light'; await changePromise; expect(await getPrefersColorSchemeIsDark(w)).to.equal(false); - changePromise = emittedOnce(ipcMain, 'theme-change'); + changePromise = once(ipcMain, 'theme-change'); nativeTheme.themeSource = 'system'; if (originalSystemIsDark) await changePromise; expect(await getPrefersColorSchemeIsDark(w)).to.equal(originalSystemIsDark); diff --git a/spec/api-net-log-spec.ts b/spec/api-net-log-spec.ts index 5ed162b805ed..7fb5d6ceeef7 100644 --- a/spec/api-net-log-spec.ts +++ b/spec/api-net-log-spec.ts @@ -7,7 +7,7 @@ import * as ChildProcess from 'child_process'; import { session, net } from 'electron/main'; import { Socket } from 'net'; import { ifit, listen } from './lib/spec-helpers'; -import { emittedOnce } from './lib/events-helpers'; +import { once } from 'events'; const appPath = path.join(__dirname, 'fixtures', 'api', 'net-log'); const dumpFile = path.join(os.tmpdir(), 'net_log.json'); @@ -127,7 +127,7 @@ describe('netLog module', () => { } }); - await emittedOnce(appProcess, 'exit'); + await once(appProcess, 'exit'); expect(fs.existsSync(dumpFile)).to.be.true('dump file exists'); }); @@ -142,7 +142,7 @@ describe('netLog module', () => { } }); - await emittedOnce(appProcess, 'exit'); + await once(appProcess, 'exit'); expect(fs.existsSync(dumpFile)).to.be.true('dump file exists'); expect(fs.existsSync(dumpFileDynamic)).to.be.true('dynamic dump file exists'); }); @@ -156,7 +156,7 @@ describe('netLog module', () => { } }); - await emittedOnce(appProcess, 'exit'); + await once(appProcess, 'exit'); expect(fs.existsSync(dumpFileDynamic)).to.be.true('dynamic dump file exists'); }); }); diff --git a/spec/api-net-spec.ts b/spec/api-net-spec.ts index d06aa93c7650..a95732551462 100644 --- a/spec/api-net-spec.ts +++ b/spec/api-net-spec.ts @@ -4,8 +4,9 @@ import { net, session, ClientRequest, BrowserWindow, ClientRequestConstructorOpt import * as http from 'http'; import * as url from 'url'; import { Socket } from 'net'; -import { emittedOnce } from './lib/events-helpers'; -import { defer, delay, listen } from './lib/spec-helpers'; +import { defer, listen } from './lib/spec-helpers'; +import { once } from 'events'; +import { setTimeout } from 'timers/promises'; // See https://github.com/nodejs/node/issues/40702. dns.setDefaultResultOrder('ipv4first'); @@ -412,9 +413,9 @@ describe('net module', () => { const urlRequest = net.request(serverUrl); // request close event - const closePromise = emittedOnce(urlRequest, 'close'); + const closePromise = once(urlRequest, 'close'); // request finish event - const finishPromise = emittedOnce(urlRequest, 'close'); + const finishPromise = once(urlRequest, 'close'); // request "response" event const response = await getResponse(urlRequest); response.on('error', (error: Error) => { @@ -1056,7 +1057,7 @@ describe('net module', () => { urlRequest.on('response', () => { expect.fail('unexpected response event'); }); - const aborted = emittedOnce(urlRequest, 'abort'); + const aborted = once(urlRequest, 'abort'); urlRequest.abort(); urlRequest.write(''); urlRequest.end(); @@ -1086,10 +1087,10 @@ describe('net module', () => { requestAbortEventEmitted = true; }); - await emittedOnce(urlRequest, 'close', () => { - urlRequest!.chunkedEncoding = true; - urlRequest!.write(randomString(kOneKiloByte)); - }); + const p = once(urlRequest, 'close'); + urlRequest.chunkedEncoding = true; + urlRequest.write(randomString(kOneKiloByte)); + await p; expect(requestReceivedByServer).to.equal(true); expect(requestAbortEventEmitted).to.equal(true); }); @@ -1119,7 +1120,7 @@ describe('net module', () => { expect.fail('Unexpected error event'); }); urlRequest.end(randomString(kOneKiloByte)); - await emittedOnce(urlRequest, 'abort'); + await once(urlRequest, 'abort'); expect(requestFinishEventEmitted).to.equal(true); expect(requestReceivedByServer).to.equal(true); }); @@ -1160,7 +1161,7 @@ describe('net module', () => { expect.fail('Unexpected error event'); }); urlRequest.end(randomString(kOneKiloByte)); - await emittedOnce(urlRequest, 'abort'); + await once(urlRequest, 'abort'); expect(requestFinishEventEmitted).to.be.true('request should emit "finish" event'); expect(requestReceivedByServer).to.be.true('request should be received by the server'); expect(requestResponseEventEmitted).to.be.true('"response" event should be emitted'); @@ -1192,7 +1193,7 @@ describe('net module', () => { abortsEmitted++; }); urlRequest.end(randomString(kOneKiloByte)); - await emittedOnce(urlRequest, 'abort'); + await once(urlRequest, 'abort'); expect(requestFinishEventEmitted).to.be.true('request should emit "finish" event'); expect(requestReceivedByServer).to.be.true('request should be received by server'); expect(abortsEmitted).to.equal(1, 'request should emit exactly 1 "abort" event'); @@ -1214,7 +1215,7 @@ describe('net module', () => { }); const eventHandlers = Promise.all([ bodyCheckPromise, - emittedOnce(urlRequest, 'close') + once(urlRequest, 'close') ]); urlRequest.end(); @@ -1445,7 +1446,7 @@ describe('net module', () => { urlRequest.end(); urlRequest.on('redirect', () => { urlRequest.abort(); }); urlRequest.on('error', () => {}); - await emittedOnce(urlRequest, 'abort'); + await once(urlRequest, 'abort'); }); it('should not follow redirect when mode is error', async () => { @@ -1459,7 +1460,7 @@ describe('net module', () => { redirect: 'error' }); urlRequest.end(); - await emittedOnce(urlRequest, 'error'); + await once(urlRequest, 'error'); }); it('should follow redirect when handler calls callback', async () => { @@ -1559,7 +1560,7 @@ describe('net module', () => { const nodeRequest = http.request(nodeServerUrl); const nodeResponse = await getResponse(nodeRequest as any) as any as http.ServerResponse; const netRequest = net.request(netServerUrl); - const responsePromise = emittedOnce(netRequest, 'response'); + const responsePromise = once(netRequest, 'response'); // TODO(@MarshallOfSound) - FIXME with #22730 nodeResponse.pipe(netRequest as any); const [netResponse] = await responsePromise; @@ -1576,7 +1577,7 @@ describe('net module', () => { const netRequest = net.request({ url: serverUrl, method: 'POST' }); expect(netRequest.getUploadProgress()).to.have.property('active', false); netRequest.end(Buffer.from('hello')); - const [position, total] = await emittedOnce(netRequest, 'upload-progress'); + const [position, total] = await once(netRequest, 'upload-progress'); expect(netRequest.getUploadProgress()).to.deep.equal({ active: true, started: true, current: position, total }); }); @@ -1586,7 +1587,7 @@ describe('net module', () => { }); const urlRequest = net.request(serverUrl); urlRequest.end(); - const [error] = await emittedOnce(urlRequest, 'error'); + const [error] = await once(urlRequest, 'error'); expect(error.message).to.equal('net::ERR_EMPTY_RESPONSE'); }); @@ -1597,7 +1598,7 @@ describe('net module', () => { }); const urlRequest = net.request(serverUrl); urlRequest.end(randomBuffer(kOneMegaByte)); - const [error] = await emittedOnce(urlRequest, 'error'); + const [error] = await once(urlRequest, 'error'); expect(error.message).to.be.oneOf(['net::ERR_FAILED', 'net::ERR_CONNECTION_RESET', 'net::ERR_CONNECTION_ABORTED']); }); @@ -1609,14 +1610,14 @@ describe('net module', () => { const urlRequest = net.request(serverUrl); urlRequest.end(); - await emittedOnce(urlRequest, 'close'); + await once(urlRequest, 'close'); await new Promise((resolve, reject) => { ['finish', 'abort', 'close', 'error'].forEach(evName => { urlRequest.on(evName as any, () => { reject(new Error(`Unexpected ${evName} event`)); }); }); - setTimeout(resolve, 50); + setTimeout(50).then(resolve); }); }); @@ -1902,7 +1903,7 @@ describe('net module', () => { port: serverUrl.port }; const nodeRequest = http.request(nodeOptions); - const nodeResponsePromise = emittedOnce(nodeRequest, 'response'); + const nodeResponsePromise = once(nodeRequest, 'response'); // TODO(@MarshallOfSound) - FIXME with #22730 (netResponse as any).pipe(nodeRequest); const [nodeResponse] = await nodeResponsePromise; @@ -1929,7 +1930,7 @@ describe('net module', () => { const urlRequest = net.request(serverUrl); urlRequest.on('response', () => {}); urlRequest.end(); - await delay(2000); + await setTimeout(2000); // TODO(nornagon): I think this ought to max out at 20, but in practice // it seems to exceed that sometimes. This is at 25 to avoid test flakes, // but we should investigate if there's actually something broken here and @@ -2159,7 +2160,7 @@ describe('net module', () => { it('should reject body promise when stream fails', async () => { const serverUrl = await respondOnce.toSingleURL((request, response) => { response.write('first chunk'); - setTimeout(() => response.destroy()); + setTimeout().then(() => response.destroy()); }); const r = await net.fetch(serverUrl); expect(r.status).to.equal(200); diff --git a/spec/api-notification-spec.ts b/spec/api-notification-spec.ts index c94ee3ab389e..b17d811cf31b 100644 --- a/spec/api-notification-spec.ts +++ b/spec/api-notification-spec.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { Notification } from 'electron/main'; -import { emittedOnce } from './lib/events-helpers'; +import { once } from 'events'; import { ifit } from './lib/spec-helpers'; describe('Notification module', () => { @@ -123,12 +123,12 @@ describe('Notification module', () => { silent: true }); { - const e = emittedOnce(n, 'show'); + const e = once(n, 'show'); n.show(); await e; } { - const e = emittedOnce(n, 'close'); + const e = once(n, 'close'); n.close(); await e; } @@ -139,7 +139,7 @@ describe('Notification module', () => { toastXml: 'not xml' }); { - const e = emittedOnce(n, 'failed'); + const e = once(n, 'failed'); n.show(); await e; } diff --git a/spec/api-power-monitor-spec.ts b/spec/api-power-monitor-spec.ts index 6a6337fddee9..470096d339fd 100644 --- a/spec/api-power-monitor-spec.ts +++ b/spec/api-power-monitor-spec.ts @@ -8,8 +8,9 @@ // python-dbusmock. import { expect } from 'chai'; import * as dbus from 'dbus-native'; -import { ifdescribe, delay } from './lib/spec-helpers'; +import { ifdescribe } from './lib/spec-helpers'; import { promisify } from 'util'; +import { setTimeout } from 'timers/promises'; describe('powerMonitor', () => { let logindMock: any, dbusMockPowerMonitor: any, getCalls: any, emitSignal: any, reset: any; @@ -59,7 +60,7 @@ describe('powerMonitor', () => { while (retriesRemaining-- > 0) { calls = await getCalls(); if (calls.length > 0) break; - await delay(1000); + await setTimeout(1000); } expect(calls).to.be.an('array').that.has.lengthOf(1); expect(calls[0].slice(1)).to.deep.equal([ diff --git a/spec/api-protocol-spec.ts b/spec/api-protocol-spec.ts index 8b5664745f49..542b3c3a2e17 100644 --- a/spec/api-protocol-spec.ts +++ b/spec/api-protocol-spec.ts @@ -7,11 +7,11 @@ import * as http from 'http'; import * as fs from 'fs'; import * as qs from 'querystring'; import * as stream from 'stream'; -import { EventEmitter } from 'events'; +import { EventEmitter, once } from 'events'; import { closeAllWindows, closeWindow } from './lib/window-helpers'; -import { emittedOnce } from './lib/events-helpers'; import { WebmGenerator } from './lib/video-helpers'; -import { delay, listen } from './lib/spec-helpers'; +import { listen } from './lib/spec-helpers'; +import { setTimeout } from 'timers/promises'; const fixturesPath = path.resolve(__dirname, 'fixtures'); @@ -37,7 +37,7 @@ function getStream (chunkSize = text.length, data: Buffer | string = text) { const body = new stream.PassThrough(); async function sendChunks () { - await delay(0); // the stream protocol API breaks if you send data immediately. + await setTimeout(0); // the stream protocol API breaks if you send data immediately. let buf = Buffer.from(data as any); // nodejs typings are wrong, Buffer.from can take a Buffer for (;;) { body.push(buf.slice(0, chunkSize)); @@ -46,7 +46,7 @@ function getStream (chunkSize = text.length, data: Buffer | string = text) { break; } // emulate some network delay - await delay(10); + await setTimeout(10); } body.push(null); } @@ -499,7 +499,7 @@ describe('protocol module', () => { data: createStream() }); }); - const hasEndedPromise = emittedOnce(events, 'end'); + const hasEndedPromise = once(events, 'end'); ajax(protocolName + '://fake-host').catch(() => {}); await hasEndedPromise; }); @@ -520,8 +520,8 @@ describe('protocol module', () => { events.emit('respond'); }); - const hasRespondedPromise = emittedOnce(events, 'respond'); - const hasClosedPromise = emittedOnce(events, 'close'); + const hasRespondedPromise = once(events, 'respond'); + const hasClosedPromise = once(events, 'close'); ajax(protocolName + '://fake-host').catch(() => {}); await hasRespondedPromise; await contents.loadFile(path.join(__dirname, 'fixtures', 'pages', 'fetch.html')); @@ -713,7 +713,7 @@ describe('protocol module', () => { it('can execute redirects', async () => { interceptStreamProtocol('http', (request, callback) => { if (request.url.indexOf('http://fake-host') === 0) { - setTimeout(() => { + setTimeout(300).then(() => { callback({ data: '', statusCode: 302, @@ -721,7 +721,7 @@ describe('protocol module', () => { Location: 'http://fake-redirect' } }); - }, 300); + }); } else { expect(request.url.indexOf('http://fake-redirect')).to.equal(0); callback(getStream(1, 'redirect')); @@ -734,14 +734,14 @@ describe('protocol module', () => { it('should discard post data after redirection', async () => { interceptStreamProtocol('http', (request, callback) => { if (request.url.indexOf('http://fake-host') === 0) { - setTimeout(() => { + setTimeout(300).then(() => { callback({ statusCode: 302, headers: { Location: 'http://fake-redirect' } }); - }, 300); + }); } else { expect(request.url.indexOf('http://fake-redirect')).to.equal(0); callback(getStream(3, request.method)); @@ -770,7 +770,7 @@ describe('protocol module', () => { let stderr = ''; appProcess.stdout.on('data', data => { process.stdout.write(data); stdout += data; }); appProcess.stderr.on('data', data => { process.stderr.write(data); stderr += data; }); - const [code] = await emittedOnce(appProcess, 'exit'); + const [code] = await once(appProcess, 'exit'); if (code !== 0) { console.log('Exit code : ', code); console.log('stdout : ', stdout); @@ -979,13 +979,13 @@ describe('protocol module', () => { newContents.on('console-message', (e, level, message) => consoleMessages.push(message)); try { newContents.loadURL(standardScheme + '://fake-host'); - const [, response] = await emittedOnce(ipcMain, 'response'); + const [, response] = await once(ipcMain, 'response'); expect(response).to.deep.equal(expected); expect(consoleMessages.join('\n')).to.match(expectedConsole); } finally { // This is called in a timeout to avoid a crash that happens when // calling destroy() in a microtask. - setTimeout(() => { + setTimeout().then(() => { newContents.destroy(); }); } @@ -1082,12 +1082,12 @@ describe('protocol module', () => { try { newContents.loadURL(testingScheme + '://fake-host'); - const [, response] = await emittedOnce(ipcMain, 'result'); + const [, response] = await once(ipcMain, 'result'); expect(response).to.deep.equal(expected); } finally { // This is called in a timeout to avoid a crash that happens when // calling destroy() in a microtask. - setTimeout(() => { + setTimeout().then(() => { newContents.destroy(); }); } diff --git a/spec/api-safe-storage-spec.ts b/spec/api-safe-storage-spec.ts index 59e7c19cddff..51278a13c79c 100644 --- a/spec/api-safe-storage-spec.ts +++ b/spec/api-safe-storage-spec.ts @@ -2,9 +2,9 @@ import * as cp from 'child_process'; import * as path from 'path'; import { safeStorage } from 'electron/main'; import { expect } from 'chai'; -import { emittedOnce } from './lib/events-helpers'; import { ifdescribe } from './lib/spec-helpers'; import * as fs from 'fs-extra'; +import { once } from 'events'; /* isEncryptionAvailable returns false in Linux when running CI due to a mocked dbus. This stops * Chrome from reaching the system's keyring or libsecret. When running the tests with config.store @@ -24,7 +24,7 @@ describe('safeStorage module', () => { appProcess.stdout.on('data', data => { output += data; }); appProcess.stderr.on('data', data => { output += data; }); - const code = (await emittedOnce(appProcess, 'exit'))[0] ?? 1; + const code = (await once(appProcess, 'exit'))[0] ?? 1; if (code !== 0 && output) { console.log(output); @@ -98,7 +98,7 @@ ifdescribe(process.platform !== 'linux')('safeStorage module', () => { encryptAppProcess.stderr.on('data', data => { stdout += data; }); try { - await emittedOnce(encryptAppProcess, 'exit'); + await once(encryptAppProcess, 'exit'); const appPath = path.join(fixturesPath, 'api', 'safe-storage', 'decrypt-app'); const relaunchedAppProcess = cp.spawn(process.execPath, [appPath]); @@ -107,7 +107,7 @@ ifdescribe(process.platform !== 'linux')('safeStorage module', () => { relaunchedAppProcess.stdout.on('data', data => { output += data; }); relaunchedAppProcess.stderr.on('data', data => { output += data; }); - const [code] = await emittedOnce(relaunchedAppProcess, 'exit'); + const [code] = await once(relaunchedAppProcess, 'exit'); if (!output.includes('plaintext')) { console.log(code, output); diff --git a/spec/api-service-workers-spec.ts b/spec/api-service-workers-spec.ts index 03932b9975ca..db68eed25183 100644 --- a/spec/api-service-workers-spec.ts +++ b/spec/api-service-workers-spec.ts @@ -4,8 +4,8 @@ import * as path from 'path'; import { session, webContents, WebContents } from 'electron/main'; import { expect } from 'chai'; import { v4 } from 'uuid'; -import { emittedOnce, emittedNTimes } from './lib/events-helpers'; import { listen } from './lib/spec-helpers'; +import { on, once } from 'events'; const partition = 'service-workers-spec'; @@ -50,7 +50,8 @@ describe('session.serviceWorkers', () => { }); it('should report one as running once you load a page with a service worker', async () => { - await emittedOnce(ses.serviceWorkers, 'console-message', () => w.loadURL(`${baseUrl}/index.html`)); + w.loadURL(`${baseUrl}/index.html`); + await once(ses.serviceWorkers, 'console-message'); const workers = ses.serviceWorkers.getAllRunning(); const ids = Object.keys(workers) as any[] as number[]; expect(ids).to.have.lengthOf(1, 'should have one worker running'); @@ -59,7 +60,8 @@ describe('session.serviceWorkers', () => { describe('getFromVersionID()', () => { it('should report the correct script url and scope', async () => { - const eventInfo = await emittedOnce(ses.serviceWorkers, 'console-message', () => w.loadURL(`${baseUrl}/index.html`)); + w.loadURL(`${baseUrl}/index.html`); + const eventInfo = await once(ses.serviceWorkers, 'console-message'); const details: Electron.MessageDetails = eventInfo[1]; const worker = ses.serviceWorkers.getFromVersionID(details.versionId); expect(worker).to.not.equal(null); @@ -71,11 +73,11 @@ describe('session.serviceWorkers', () => { describe('console-message event', () => { it('should correctly keep the source, message and level', async () => { const messages: Record = {}; - const events = await emittedNTimes(ses.serviceWorkers, 'console-message', 4, () => w.loadURL(`${baseUrl}/logs.html`)); - for (const event of events) { - messages[event[1].message] = event[1]; - - expect(event[1]).to.have.property('source', 'console-api'); + w.loadURL(`${baseUrl}/logs.html`); + for await (const [, details] of on(ses.serviceWorkers, 'console-message')) { + messages[details.message] = details; + expect(details).to.have.property('source', 'console-api'); + if (Object.keys(messages).length >= 4) break; } expect(messages).to.have.property('log log'); diff --git a/spec/api-session-spec.ts b/spec/api-session-spec.ts index c82823c1947b..947363e61569 100644 --- a/spec/api-session-spec.ts +++ b/spec/api-session-spec.ts @@ -8,8 +8,9 @@ import { app, session, BrowserWindow, net, ipcMain, Session, webFrameMain, WebFr import * as send from 'send'; import * as auth from 'basic-auth'; import { closeAllWindows } from './lib/window-helpers'; -import { emittedOnce } from './lib/events-helpers'; -import { defer, delay, listen } from './lib/spec-helpers'; +import { defer, listen } from './lib/spec-helpers'; +import { once } from 'events'; +import { setTimeout } from 'timers/promises'; /* The whole session API doesn't use standard callbacks */ /* eslint-disable standard/no-callback-literal */ @@ -184,11 +185,11 @@ describe('session module', () => { const name = 'foo'; const value = 'bar'; - const a = emittedOnce(cookies, 'changed'); + const a = once(cookies, 'changed'); await cookies.set({ url, name, value, expirationDate: (+new Date()) / 1000 + 120 }); const [, setEventCookie, setEventCause, setEventRemoved] = await a; - const b = emittedOnce(cookies, 'changed'); + const b = once(cookies, 'changed'); await cookies.remove(url, name); const [, removeEventCookie, removeEventCause, removeEventRemoved] = await b; @@ -331,7 +332,7 @@ describe('session module', () => { customSession = session.fromPartition(partitionName); await customSession.protocol.registerStringProtocol(protocolName, handler); w.loadURL(`${protocolName}://fake-host`); - await emittedOnce(ipcMain, 'hello'); + await once(ipcMain, 'hello'); }); }); @@ -345,7 +346,7 @@ describe('session module', () => { if (!created) { // Work around for https://github.com/electron/electron/issues/26166 to // reduce flake - await delay(100); + await setTimeout(100); created = true; } }); @@ -654,7 +655,7 @@ describe('session module', () => { const w = new BrowserWindow({ show: false, webPreferences: { session: ses } }); await expect(w.loadURL(serverUrl), 'first load').to.eventually.be.rejectedWith(/ERR_FAILED/); - await emittedOnce(w.webContents, 'did-stop-loading'); + await once(w.webContents, 'did-stop-loading'); await expect(w.loadURL(serverUrl + '/test'), 'second load').to.eventually.be.rejectedWith(/ERR_FAILED/); expect(w.webContents.getTitle()).to.equal(serverUrl + '/test'); expect(numVerificationRequests).to.equal(1); @@ -667,7 +668,7 @@ describe('session module', () => { const req = net.request({ url: serverUrl, session: ses1, credentials: 'include' }); req.end(); - setTimeout(() => { + setTimeout().then(() => { ses2.setCertificateVerifyProc((opts, callback) => callback(0)); }); await expect(new Promise((resolve, reject) => { @@ -963,7 +964,7 @@ describe('session module', () => { length: 5242880 }; const w = new BrowserWindow({ show: false }); - const p = emittedOnce(w.webContents.session, 'will-download'); + const p = once(w.webContents.session, 'will-download'); w.webContents.session.createInterruptedDownload(options); const [, item] = await p; expect(item.getState()).to.equal('interrupted'); @@ -1070,7 +1071,7 @@ describe('session module', () => { cb(``); }); - const result = emittedOnce(require('electron').ipcMain, 'message'); + const result = once(require('electron').ipcMain, 'message'); function remote () { (navigator as any).requestMIDIAccess({ sysex: true }).then(() => {}, (err: any) => { @@ -1197,7 +1198,7 @@ describe('session module', () => { document.body.appendChild(iframe); null; `); - const [,, frameProcessId, frameRoutingId] = await emittedOnce(w.webContents, 'did-frame-finish-load'); + const [,, frameProcessId, frameRoutingId] = await once(w.webContents, 'did-frame-finish-load'); const state = await readClipboardPermission(webFrameMain.fromId(frameProcessId, frameRoutingId)); expect(state).to.equal('granted'); expect(handlerDetails!.requestingUrl).to.equal(loadUrl); @@ -1260,7 +1261,7 @@ describe('session module', () => { describe('session-created event', () => { it('is emitted when a session is created', async () => { - const sessionCreated = emittedOnce(app, 'session-created'); + const sessionCreated = once(app, 'session-created'); const session1 = session.fromPartition('' + Math.random()); const [session2] = await sessionCreated; expect(session1).to.equal(session2); diff --git a/spec/api-shell-spec.ts b/spec/api-shell-spec.ts index 2dc883dc8783..eddf372939e3 100644 --- a/spec/api-shell-spec.ts +++ b/spec/api-shell-spec.ts @@ -1,13 +1,13 @@ import { BrowserWindow, app } from 'electron/main'; import { shell } from 'electron/common'; import { closeAllWindows } from './lib/window-helpers'; -import { emittedOnce } from './lib/events-helpers'; import { ifdescribe, ifit, listen } from './lib/spec-helpers'; import * as http from 'http'; import * as fs from 'fs-extra'; import * as os from 'os'; import * as path from 'path'; import { expect } from 'chai'; +import { once } from 'events'; describe('shell module', () => { describe('shell.openExternal()', () => { @@ -45,7 +45,7 @@ describe('shell module', () => { // https://github.com/electron/electron/pull/19969#issuecomment-526278890), // so use a blur event as a crude proxy. const w = new BrowserWindow({ show: true }); - requestReceived = emittedOnce(w, 'blur'); + requestReceived = once(w, 'blur'); } else { const server = http.createServer((req, res) => { res.end(); diff --git a/spec/api-subframe-spec.ts b/spec/api-subframe-spec.ts index d9b4b198e118..af9933609d07 100644 --- a/spec/api-subframe-spec.ts +++ b/spec/api-subframe-spec.ts @@ -1,10 +1,11 @@ import { expect } from 'chai'; import * as path from 'path'; import * as http from 'http'; -import { emittedNTimes, emittedOnce } from './lib/events-helpers'; +import { emittedNTimes } from './lib/events-helpers'; import { closeWindow } from './lib/window-helpers'; import { app, BrowserWindow, ipcMain } from 'electron/main'; import { ifdescribe, listen } from './lib/spec-helpers'; +import { once } from 'events'; describe('renderer nodeIntegrationInSubFrames', () => { const generateTests = (description: string, webPreferences: any) => { @@ -57,7 +58,7 @@ describe('renderer nodeIntegrationInSubFrames', () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const [event1] = await detailsPromise; - const pongPromise = emittedOnce(ipcMain, 'preload-pong'); + const pongPromise = once(ipcMain, 'preload-pong'); event1[0].reply('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event1[0].frameId); @@ -67,7 +68,7 @@ describe('renderer nodeIntegrationInSubFrames', () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const [event1] = await detailsPromise; - const pongPromise = emittedOnce(ipcMain, 'preload-pong'); + const pongPromise = once(ipcMain, 'preload-pong'); event1[0].senderFrame.send('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event1[0].frameId); @@ -77,7 +78,7 @@ describe('renderer nodeIntegrationInSubFrames', () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const [, event2] = await detailsPromise; - const pongPromise = emittedOnce(ipcMain, 'preload-pong'); + const pongPromise = once(ipcMain, 'preload-pong'); event2[0].reply('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event2[0].frameId); @@ -87,7 +88,7 @@ describe('renderer nodeIntegrationInSubFrames', () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 2); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-container${fixtureSuffix}.html`)); const [, event2] = await detailsPromise; - const pongPromise = emittedOnce(ipcMain, 'preload-pong'); + const pongPromise = once(ipcMain, 'preload-pong'); event2[0].senderFrame.send('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event2[0].frameId); @@ -97,7 +98,7 @@ describe('renderer nodeIntegrationInSubFrames', () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 3); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`)); const [, , event3] = await detailsPromise; - const pongPromise = emittedOnce(ipcMain, 'preload-pong'); + const pongPromise = once(ipcMain, 'preload-pong'); event3[0].reply('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event3[0].frameId); @@ -107,7 +108,7 @@ describe('renderer nodeIntegrationInSubFrames', () => { const detailsPromise = emittedNTimes(ipcMain, 'preload-ran', 3); w.loadFile(path.resolve(__dirname, `fixtures/sub-frames/frame-with-frame-container${fixtureSuffix}.html`)); const [, , event3] = await detailsPromise; - const pongPromise = emittedOnce(ipcMain, 'preload-pong'); + const pongPromise = once(ipcMain, 'preload-pong'); event3[0].senderFrame.send('preload-ping'); const [, frameId] = await pongPromise; expect(frameId).to.equal(event3[0].frameId); @@ -201,8 +202,8 @@ describe('renderer nodeIntegrationInSubFrames', () => { }); it('should not load preload scripts', async () => { - const promisePass = emittedOnce(ipcMain, 'webview-loaded'); - const promiseFail = emittedOnce(ipcMain, 'preload-in-frame').then(() => { + const promisePass = once(ipcMain, 'webview-loaded'); + const promiseFail = once(ipcMain, 'preload-in-frame').then(() => { throw new Error('preload loaded in internal frame'); }); await w.loadURL('about:blank'); diff --git a/spec/api-utility-process-spec.ts b/spec/api-utility-process-spec.ts index 9d072b31f5c4..5c511877e8bb 100644 --- a/spec/api-utility-process-spec.ts +++ b/spec/api-utility-process-spec.ts @@ -2,9 +2,9 @@ import { expect } from 'chai'; import * as childProcess from 'child_process'; import * as path from 'path'; import { BrowserWindow, MessageChannelMain, utilityProcess } from 'electron/main'; -import { emittedOnce } from './lib/events-helpers'; import { ifit } from './lib/spec-helpers'; import { closeWindow } from './lib/window-helpers'; +import { once } from 'events'; const fixturesPath = path.resolve(__dirname, 'fixtures', 'api', 'utility-process'); const isWindowsOnArm = process.platform === 'win32' && process.arch === 'arm64'; @@ -55,12 +55,12 @@ describe('utilityProcess module', () => { describe('lifecycle events', () => { it('emits \'spawn\' when child process successfully launches', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'empty.js')); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); }); it('emits \'exit\' when child process exits gracefully', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'empty.js')); - const [code] = await emittedOnce(child, 'exit'); + const [code] = await once(child, 'exit'); expect(code).to.equal(0); }); @@ -68,28 +68,28 @@ describe('utilityProcess module', () => { const child = utilityProcess.fork(path.join(fixturesPath, 'crash.js')); // Do not check for exit code in this case, // SIGSEGV code can be 139 or 11 across our different CI pipeline. - await emittedOnce(child, 'exit'); + await once(child, 'exit'); }); it('emits \'exit\' corresponding to the child process', async () => { const child1 = utilityProcess.fork(path.join(fixturesPath, 'endless.js')); - await emittedOnce(child1, 'spawn'); + await once(child1, 'spawn'); const child2 = utilityProcess.fork(path.join(fixturesPath, 'crash.js')); - await emittedOnce(child2, 'exit'); + await once(child2, 'exit'); expect(child1.kill()).to.be.true(); - await emittedOnce(child1, 'exit'); + await once(child1, 'exit'); }); it('emits \'exit\' when there is uncaught exception', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'exception.js')); - const [code] = await emittedOnce(child, 'exit'); + const [code] = await once(child, 'exit'); expect(code).to.equal(1); }); it('emits \'exit\' when process.exit is called', async () => { const exitCode = 2; const child = utilityProcess.fork(path.join(fixturesPath, 'custom-exit.js'), [`--exitCode=${exitCode}`]); - const [code] = await emittedOnce(child, 'exit'); + const [code] = await once(child, 'exit'); expect(code).to.equal(exitCode); }); }); @@ -99,16 +99,16 @@ describe('utilityProcess module', () => { const child = utilityProcess.fork(path.join(fixturesPath, 'endless.js'), [], { serviceName: 'endless' }); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); expect(child.kill()).to.be.true(); - await emittedOnce(child, 'exit'); + await once(child, 'exit'); }); }); describe('pid property', () => { it('is valid when child process launches successfully', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'empty.js')); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); expect(child.pid).to.not.be.null(); }); @@ -121,33 +121,33 @@ describe('utilityProcess module', () => { describe('stdout property', () => { it('is null when child process launches with default stdio', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'log.js')); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); expect(child.stdout).to.be.null(); expect(child.stderr).to.be.null(); - await emittedOnce(child, 'exit'); + await once(child, 'exit'); }); it('is null when child process launches with ignore stdio configuration', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], { stdio: 'ignore' }); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); expect(child.stdout).to.be.null(); expect(child.stderr).to.be.null(); - await emittedOnce(child, 'exit'); + await once(child, 'exit'); }); it('is valid when child process launches with pipe stdio configuration', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], { stdio: 'pipe' }); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); expect(child.stdout).to.not.be.null(); let log = ''; child.stdout!.on('data', (chunk) => { log += chunk.toString('utf8'); }); - await emittedOnce(child, 'exit'); + await once(child, 'exit'); expect(log).to.equal('hello\n'); }); }); @@ -155,32 +155,32 @@ describe('utilityProcess module', () => { describe('stderr property', () => { it('is null when child process launches with default stdio', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'log.js')); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); expect(child.stdout).to.be.null(); expect(child.stderr).to.be.null(); - await emittedOnce(child, 'exit'); + await once(child, 'exit'); }); it('is null when child process launches with ignore stdio configuration', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], { stdio: 'ignore' }); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); expect(child.stderr).to.be.null(); - await emittedOnce(child, 'exit'); + await once(child, 'exit'); }); ifit(!isWindowsOnArm)('is valid when child process launches with pipe stdio configuration', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'log.js'), [], { stdio: ['ignore', 'pipe', 'pipe'] }); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); expect(child.stderr).to.not.be.null(); let log = ''; child.stderr!.on('data', (chunk) => { log += chunk.toString('utf8'); }); - await emittedOnce(child, 'exit'); + await once(child, 'exit'); expect(log).to.equal('world'); }); }); @@ -189,25 +189,25 @@ describe('utilityProcess module', () => { it('establishes a default ipc channel with the child process', async () => { const result = 'I will be echoed.'; const child = utilityProcess.fork(path.join(fixturesPath, 'post-message.js')); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); child.postMessage(result); - const [data] = await emittedOnce(child, 'message'); + const [data] = await once(child, 'message'); expect(data).to.equal(result); - const exit = emittedOnce(child, 'exit'); + const exit = once(child, 'exit'); expect(child.kill()).to.be.true(); await exit; }); it('supports queuing messages on the receiving end', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'post-message-queue.js')); - const p = emittedOnce(child, 'spawn'); + const p = once(child, 'spawn'); child.postMessage('This message'); child.postMessage(' is'); child.postMessage(' queued'); await p; - const [data] = await emittedOnce(child, 'message'); + const [data] = await once(child, 'message'); expect(data).to.equal('This message is queued'); - const exit = emittedOnce(child, 'exit'); + const exit = once(child, 'exit'); expect(child.kill()).to.be.true(); await exit; }); @@ -270,7 +270,7 @@ describe('utilityProcess module', () => { const appProcess = childProcess.spawn(process.execPath, [path.join(fixturesPath, 'inherit-stdout'), `--payload=${result}`]); let output = ''; appProcess.stdout.on('data', (data: Buffer) => { output += data; }); - await emittedOnce(appProcess, 'exit'); + await once(appProcess, 'exit'); expect(output).to.equal(result); }); @@ -279,7 +279,7 @@ describe('utilityProcess module', () => { const appProcess = childProcess.spawn(process.execPath, [path.join(fixturesPath, 'inherit-stderr'), `--payload=${result}`]); let output = ''; appProcess.stderr.on('data', (data: Buffer) => { output += data; }); - await emittedOnce(appProcess, 'exit'); + await once(appProcess, 'exit'); expect(output).to.include(result); }); @@ -297,12 +297,12 @@ describe('utilityProcess module', () => { w.webContents.postMessage('port', result, [rendererPort]); // Send renderer and main channel port to utility process. const child = utilityProcess.fork(path.join(fixturesPath, 'receive-message.js')); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); child.postMessage('', [childPort1]); - const [data] = await emittedOnce(child, 'message'); + const [data] = await once(child, 'message'); expect(data).to.equal(result); // Cleanup. - const exit = emittedOnce(child, 'exit'); + const exit = once(child, 'exit'); expect(child.kill()).to.be.true(); await exit; await closeWindow(w); @@ -310,10 +310,10 @@ describe('utilityProcess module', () => { ifit(process.platform === 'linux')('allows executing a setuid binary with child_process', async () => { const child = utilityProcess.fork(path.join(fixturesPath, 'suid.js')); - await emittedOnce(child, 'spawn'); - const [data] = await emittedOnce(child, 'message'); + await once(child, 'spawn'); + const [data] = await once(child, 'message'); expect(data).to.not.be.empty(); - const exit = emittedOnce(child, 'exit'); + const exit = once(child, 'exit'); expect(child.kill()).to.be.true(); await exit; }); @@ -327,7 +327,7 @@ describe('utilityProcess module', () => { }); let output = ''; appProcess.stdout.on('data', (data: Buffer) => { output += data; }); - await emittedOnce(appProcess.stdout, 'end'); + await once(appProcess.stdout, 'end'); const result = process.platform === 'win32' ? '\r\nparent' : 'parent'; expect(output).to.equal(result); }); @@ -341,7 +341,7 @@ describe('utilityProcess module', () => { }); let output = ''; appProcess.stdout.on('data', (data: Buffer) => { output += data; }); - await emittedOnce(appProcess.stdout, 'end'); + await once(appProcess.stdout, 'end'); const result = process.platform === 'win32' ? '\r\nchild' : 'child'; expect(output).to.equal(result); }); @@ -351,13 +351,13 @@ describe('utilityProcess module', () => { cwd: fixturesPath, stdio: ['ignore', 'pipe', 'ignore'] }); - await emittedOnce(child, 'spawn'); + await once(child, 'spawn'); expect(child.stdout).to.not.be.null(); let log = ''; child.stdout!.on('data', (chunk) => { log += chunk.toString('utf8'); }); - await emittedOnce(child, 'exit'); + await once(child, 'exit'); expect(log).to.equal('hello\n'); }); }); diff --git a/spec/api-web-contents-spec.ts b/spec/api-web-contents-spec.ts index 9406d876981e..ae5404912d1e 100644 --- a/spec/api-web-contents-spec.ts +++ b/spec/api-web-contents-spec.ts @@ -4,9 +4,10 @@ import * as path from 'path'; import * as fs from 'fs'; import * as http from 'http'; import { BrowserWindow, ipcMain, webContents, session, app, BrowserView } from 'electron/main'; -import { emittedOnce } from './lib/events-helpers'; import { closeAllWindows } from './lib/window-helpers'; -import { ifdescribe, delay, defer, waitUntil, listen } from './lib/spec-helpers'; +import { ifdescribe, defer, waitUntil, listen } from './lib/spec-helpers'; +import { once } from 'events'; +import { setTimeout } from 'timers/promises'; const pdfjs = require('pdfjs-dist'); const fixturesPath = path.resolve(__dirname, 'fixtures'); @@ -23,11 +24,11 @@ describe('webContents module', () => { }); w.loadFile(path.join(fixturesPath, 'pages', 'webview-zoom-factor.html')); - await emittedOnce(w.webContents, 'did-attach-webview'); + await once(w.webContents, 'did-attach-webview'); w.webContents.openDevTools(); - await emittedOnce(w.webContents, 'devtools-opened'); + await once(w.webContents, 'devtools-opened'); const all = webContents.getAllWebContents().sort((a, b) => { return a.id - b.id; @@ -94,7 +95,7 @@ describe('webContents module', () => { expect.fail('should not have fired'); }); await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-undefined.html')); - const wait = emittedOnce(w, 'closed'); + const wait = once(w, 'closed'); w.close(); await wait; }); @@ -110,7 +111,7 @@ describe('webContents module', () => { }); await view.webContents.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-undefined.html')); - const wait = emittedOnce(w, 'closed'); + const wait = once(w, 'closed'); w.close(); await wait; }); @@ -119,7 +120,7 @@ describe('webContents module', () => { const w = new BrowserWindow({ show: false }); await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html')); w.close(); - await emittedOnce(w.webContents, 'will-prevent-unload'); + await once(w.webContents, 'will-prevent-unload'); }); it('emits if beforeunload returns false in a BrowserView', async () => { @@ -130,14 +131,14 @@ describe('webContents module', () => { await view.webContents.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html')); w.close(); - await emittedOnce(view.webContents, 'will-prevent-unload'); + await once(view.webContents, 'will-prevent-unload'); }); it('supports calling preventDefault on will-prevent-unload events in a BrowserWindow', async () => { const w = new BrowserWindow({ show: false }); w.webContents.once('will-prevent-unload', event => event.preventDefault()); await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'beforeunload-false.html')); - const wait = emittedOnce(w, 'closed'); + const wait = once(w, 'closed'); w.close(); await wait; }); @@ -171,9 +172,9 @@ describe('webContents module', () => { } }); w.loadFile(path.join(fixturesPath, 'pages', 'send-after-node.html')); - setTimeout(() => { + setTimeout(50).then(() => { w.webContents.send('test'); - }, 50); + }); }); }); @@ -362,7 +363,7 @@ describe('webContents module', () => { it('resolves when navigating within the page', async () => { await w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html')); - await delay(); + await setTimeout(); await expect(w.loadURL(w.getURL() + '#foo')).to.eventually.be.fulfilled(); }); @@ -494,11 +495,11 @@ describe('webContents module', () => { await w.loadFile(path.join(__dirname, 'fixtures', 'blank.html')); expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.id); - const devToolsOpened = emittedOnce(w.webContents, 'devtools-opened'); + const devToolsOpened = once(w.webContents, 'devtools-opened'); w.webContents.openDevTools(); await devToolsOpened; expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.devToolsWebContents!.id); - const devToolsClosed = emittedOnce(w.webContents, 'devtools-closed'); + const devToolsClosed = once(w.webContents, 'devtools-closed'); w.webContents.closeDevTools(); await devToolsClosed; expect(webContents.getFocusedWebContents().id).to.equal(w.webContents.id); @@ -511,14 +512,14 @@ describe('webContents module', () => { w.webContents.inspectElement(100, 100); // For some reason we have to wait for two focused events...? - await emittedOnce(w.webContents, 'devtools-focused'); + await once(w.webContents, 'devtools-focused'); expect(() => { webContents.getFocusedWebContents(); }).to.not.throw(); // Work around https://github.com/electron/electron/issues/19985 - await delay(); + await setTimeout(); - const devToolsClosed = emittedOnce(w.webContents, 'devtools-closed'); + const devToolsClosed = once(w.webContents, 'devtools-closed'); w.webContents.closeDevTools(); await devToolsClosed; expect(() => { webContents.getFocusedWebContents(); }).to.not.throw(); @@ -530,7 +531,7 @@ describe('webContents module', () => { it('sets arbitrary webContents as devtools', async () => { const w = new BrowserWindow({ show: false }); const devtools = new BrowserWindow({ show: false }); - const promise = emittedOnce(devtools.webContents, 'dom-ready'); + const promise = once(devtools.webContents, 'dom-ready'); w.webContents.setDevToolsWebContents(devtools.webContents); w.webContents.openDevTools(); await promise; @@ -564,11 +565,11 @@ describe('webContents module', () => { oscillator.connect(context.destination) oscillator.start() `); - let p = emittedOnce(w.webContents, '-audio-state-changed'); + let p = once(w.webContents, '-audio-state-changed'); w.webContents.executeJavaScript('context.resume()'); await p; expect(w.webContents.isCurrentlyAudible()).to.be.true(); - p = emittedOnce(w.webContents, '-audio-state-changed'); + p = once(w.webContents, '-audio-state-changed'); w.webContents.executeJavaScript('oscillator.stop()'); await p; expect(w.webContents.isCurrentlyAudible()).to.be.false(); @@ -579,15 +580,15 @@ describe('webContents module', () => { afterEach(closeAllWindows); it('can show window with activation', async () => { const w = new BrowserWindow({ show: false }); - const focused = emittedOnce(w, 'focus'); + const focused = once(w, 'focus'); w.show(); await focused; expect(w.isFocused()).to.be.true(); - const blurred = emittedOnce(w, 'blur'); + const blurred = once(w, 'blur'); w.webContents.openDevTools({ mode: 'detach', activate: true }); await Promise.all([ - emittedOnce(w.webContents, 'devtools-opened'), - emittedOnce(w.webContents, 'devtools-focused') + once(w.webContents, 'devtools-opened'), + once(w.webContents, 'devtools-focused') ]); await blurred; expect(w.isFocused()).to.be.false(); @@ -595,7 +596,7 @@ describe('webContents module', () => { it('can show window without activation', async () => { const w = new BrowserWindow({ show: false }); - const devtoolsOpened = emittedOnce(w.webContents, 'devtools-opened'); + const devtoolsOpened = once(w.webContents, 'devtools-opened'); w.webContents.openDevTools({ mode: 'detach', activate: false }); await devtoolsOpened; expect(w.webContents.isDevToolsOpened()).to.be.true(); @@ -629,7 +630,7 @@ describe('webContents module', () => { if (opts.meta) modifiers.push('meta'); if (opts.isAutoRepeat) modifiers.push('isAutoRepeat'); - const p = emittedOnce(w.webContents, 'before-input-event'); + const p = once(w.webContents, 'before-input-event'); w.webContents.sendInputEvent({ type: opts.type, keyCode: opts.keyCode, @@ -712,7 +713,7 @@ describe('webContents module', () => { modifiers: ['control', 'meta'] }); - const [, zoomDirection] = await emittedOnce(w.webContents, 'zoom-changed'); + const [, zoomDirection] = await once(w.webContents, 'zoom-changed'); expect(zoomDirection).to.equal('in'); }; @@ -735,7 +736,7 @@ describe('webContents module', () => { modifiers: ['control', 'meta'] }); - const [, zoomDirection] = await emittedOnce(w.webContents, 'zoom-changed'); + const [, zoomDirection] = await once(w.webContents, 'zoom-changed'); expect(zoomDirection).to.equal('out'); }; @@ -752,7 +753,7 @@ describe('webContents module', () => { afterEach(closeAllWindows); it('can send keydown events', async () => { - const keydown = emittedOnce(ipcMain, 'keydown'); + const keydown = once(ipcMain, 'keydown'); w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'A' }); const [, key, code, keyCode, shiftKey, ctrlKey, altKey] = await keydown; expect(key).to.equal('a'); @@ -764,7 +765,7 @@ describe('webContents module', () => { }); it('can send keydown events with modifiers', async () => { - const keydown = emittedOnce(ipcMain, 'keydown'); + const keydown = once(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'); @@ -776,7 +777,7 @@ describe('webContents module', () => { }); it('can send keydown events with special keys', async () => { - const keydown = emittedOnce(ipcMain, 'keydown'); + const keydown = once(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'); @@ -788,7 +789,7 @@ describe('webContents module', () => { }); it('can send char events', async () => { - const keypress = emittedOnce(ipcMain, 'keypress'); + const keypress = once(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; @@ -801,7 +802,7 @@ describe('webContents module', () => { }); it('can send char events with modifiers', async () => { - const keypress = emittedOnce(ipcMain, 'keypress'); + const keypress = once(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; @@ -839,7 +840,7 @@ describe('webContents module', () => { it('supports inspecting an element in the devtools', async () => { const w = new BrowserWindow({ show: false }); w.loadURL('about:blank'); - const event = emittedOnce(w.webContents, 'devtools-opened'); + const event = once(w.webContents, 'devtools-opened'); w.webContents.inspectElement(10, 10); await event; }); @@ -882,7 +883,7 @@ describe('webContents module', () => { }); const moveFocusToDevTools = async (win: BrowserWindow) => { - const devToolsOpened = emittedOnce(win.webContents, 'devtools-opened'); + const devToolsOpened = once(win.webContents, 'devtools-opened'); win.webContents.openDevTools({ mode: 'right' }); await devToolsOpened; win.webContents.devToolsWebContents!.focus(); @@ -895,7 +896,7 @@ describe('webContents module', () => { const w = new BrowserWindow({ show: false }); await w.loadURL('about:blank'); await moveFocusToDevTools(w); - const focusPromise = emittedOnce(w.webContents, 'focus'); + const focusPromise = once(w.webContents, 'focus'); w.webContents.focus(); await expect(focusPromise).to.eventually.be.fulfilled(); }); @@ -907,7 +908,7 @@ describe('webContents module', () => { const w = new BrowserWindow({ show: true }); await w.loadURL('about:blank'); w.webContents.focus(); - const blurPromise = emittedOnce(w.webContents, 'blur'); + const blurPromise = once(w.webContents, 'blur'); await moveFocusToDevTools(w); await expect(blurPromise).to.eventually.be.fulfilled(); }); @@ -1175,9 +1176,9 @@ describe('webContents module', () => { it('can persist when it contains iframe', (done) => { const w = new BrowserWindow({ show: false }); const server = http.createServer((req, res) => { - setTimeout(() => { + setTimeout(200).then(() => { res.end(); - }, 200); + }); }); server.listen(0, '127.0.0.1', () => { const url = 'http://127.0.0.1:' + (server.address() as AddressInfo).port; @@ -1208,7 +1209,7 @@ describe('webContents module', () => { const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); const w2 = new BrowserWindow({ show: false }); - const temporaryZoomSet = emittedOnce(ipcMain, 'temporary-zoom-set'); + const temporaryZoomSet = once(ipcMain, 'temporary-zoom-set'); w.loadFile(path.join(fixturesPath, 'pages', 'webframe-zoom.html')); await temporaryZoomSet; @@ -1232,7 +1233,7 @@ describe('webContents module', () => { before(async () => { server = http.createServer((req, res) => { - setTimeout(() => res.end('hey'), 0); + setTimeout().then(() => res.end('hey')); }); serverUrl = (await listen(server)).url; crossSiteUrl = serverUrl.replace('127.0.0.1', 'localhost'); @@ -1249,12 +1250,12 @@ describe('webContents module', () => { webFrame.setZoomLevel(0.6) ipcRenderer.send('zoom-level-set', webFrame.getZoomLevel()) `; - const zoomLevelPromise = emittedOnce(ipcMain, 'zoom-level-set'); + const zoomLevelPromise = once(ipcMain, 'zoom-level-set'); await w.loadURL(serverUrl); await w.webContents.executeJavaScript(source); let [, zoomLevel] = await zoomLevelPromise; expect(zoomLevel).to.equal(0.6); - const loadPromise = emittedOnce(w.webContents, 'did-finish-load'); + const loadPromise = once(w.webContents, 'did-finish-load'); await w.loadURL(crossSiteUrl); await loadPromise; zoomLevel = w.webContents.zoomLevel; @@ -1285,7 +1286,7 @@ describe('webContents module', () => { it('can get opener with window.open()', async () => { const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }); await w.loadURL('about:blank'); - const childPromise = emittedOnce(w.webContents, 'did-create-window'); + const childPromise = once(w.webContents, 'did-create-window'); w.webContents.executeJavaScript('window.open("about:blank")', true); const [childWindow] = await childPromise; expect(childWindow.webContents.opener).to.equal(w.webContents.mainFrame); @@ -1293,7 +1294,7 @@ describe('webContents module', () => { it('has no opener when using "noopener"', async () => { const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }); await w.loadURL('about:blank'); - const childPromise = emittedOnce(w.webContents, 'did-create-window'); + const childPromise = once(w.webContents, 'did-create-window'); w.webContents.executeJavaScript('window.open("about:blank", undefined, "noopener")', true); const [childWindow] = await childPromise; expect(childWindow.webContents.opener).to.be.null(); @@ -1301,7 +1302,7 @@ describe('webContents module', () => { it('can get opener with a[target=_blank][rel=opener]', async () => { const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }); await w.loadURL('about:blank'); - const childPromise = emittedOnce(w.webContents, 'did-create-window'); + const childPromise = once(w.webContents, 'did-create-window'); w.webContents.executeJavaScript(`(function() { const a = document.createElement('a'); a.target = '_blank'; @@ -1315,7 +1316,7 @@ describe('webContents module', () => { it('has no opener with a[target=_blank][rel=noopener]', async () => { const w = new BrowserWindow({ show: false, webPreferences: { sandbox: true } }); await w.loadURL('about:blank'); - const childPromise = emittedOnce(w.webContents, 'did-create-window'); + const childPromise = once(w.webContents, 'did-create-window'); w.webContents.executeJavaScript(`(function() { const a = document.createElement('a'); a.target = '_blank'; @@ -1350,7 +1351,7 @@ describe('webContents module', () => { res.end(); } }; - setTimeout(respond, 0); + setTimeout().then(respond); }); serverUrl = (await listen(server)).url; crossSiteUrl = serverUrl.replace('127.0.0.1', 'localhost'); @@ -1373,7 +1374,7 @@ describe('webContents module', () => { w.webContents.removeListener('current-render-view-deleted' as any, renderViewDeletedHandler); w.close(); }); - const destroyed = emittedOnce(w.webContents, 'destroyed'); + const destroyed = once(w.webContents, 'destroyed'); w.loadURL(`${serverUrl}/redirect-cross-site`); await destroyed; expect(currentRenderViewDeletedEmitted).to.be.false('current-render-view-deleted was emitted'); @@ -1383,7 +1384,7 @@ describe('webContents module', () => { const parentWindow = new BrowserWindow({ show: false }); let currentRenderViewDeletedEmitted = false; let childWindow: BrowserWindow | null = null; - const destroyed = emittedOnce(parentWindow.webContents, 'destroyed'); + const destroyed = once(parentWindow.webContents, 'destroyed'); const renderViewDeletedHandler = () => { currentRenderViewDeletedEmitted = true; }; @@ -1411,7 +1412,7 @@ describe('webContents module', () => { w.webContents.on('did-finish-load', () => { w.close(); }); - const destroyed = emittedOnce(w.webContents, 'destroyed'); + const destroyed = once(w.webContents, 'destroyed'); w.loadURL(`${serverUrl}/redirect-cross-site`); await destroyed; expect(currentRenderViewDeletedEmitted).to.be.true('current-render-view-deleted wasn\'t emitted'); @@ -1426,7 +1427,7 @@ describe('webContents module', () => { w.webContents.on('did-finish-load', () => { w.close(); }); - const destroyed = emittedOnce(w.webContents, 'destroyed'); + const destroyed = once(w.webContents, 'destroyed'); w.loadURL(`${serverUrl}/redirect-cross-site`); await destroyed; const expectedRenderViewDeletedEventCount = 1; @@ -1477,7 +1478,7 @@ describe('webContents module', () => { it('forcefullyCrashRenderer() crashes the process with reason=killed||crashed', async () => { expect(w.webContents.isCrashed()).to.equal(false); - const crashEvent = emittedOnce(w.webContents, 'render-process-gone'); + const crashEvent = once(w.webContents, 'render-process-gone'); w.webContents.forcefullyCrashRenderer(); const [, details] = await crashEvent; expect(details.reason === 'killed' || details.reason === 'crashed').to.equal(true, 'reason should be killed || crashed'); @@ -1543,7 +1544,7 @@ describe('webContents module', () => { const originalEmit = contents.emit.bind(contents); contents.emit = (...args) => { return originalEmit(...args); }; contents.once(e.name as any, () => contents.destroy()); - const destroyed = emittedOnce(contents, 'destroyed'); + const destroyed = once(contents, 'destroyed'); contents.loadURL(serverUrl + e.url); await destroyed; }); @@ -1596,7 +1597,7 @@ describe('webContents module', () => { require('electron').ipcRenderer.send('message', 'Hello World!') `); - const [, channel, message] = await emittedOnce(w.webContents, 'ipc-message'); + const [, channel, message] = await once(w.webContents, 'ipc-message'); expect(channel).to.equal('message'); expect(message).to.equal('Hello World!'); }); @@ -1708,7 +1709,7 @@ describe('webContents module', () => { } }); - const promise = emittedOnce(w.webContents, 'preload-error'); + const promise = once(w.webContents, 'preload-error'); w.loadURL('about:blank'); const [, preloadPath, error] = await promise; @@ -1727,7 +1728,7 @@ describe('webContents module', () => { } }); - const promise = emittedOnce(w.webContents, 'preload-error'); + const promise = once(w.webContents, 'preload-error'); w.loadURL('about:blank'); const [, preloadPath, error] = await promise; @@ -1746,7 +1747,7 @@ describe('webContents module', () => { } }); - const promise = emittedOnce(w.webContents, 'preload-error'); + const promise = once(w.webContents, 'preload-error'); w.loadURL('about:blank'); const [, preloadPath, error] = await promise; @@ -2061,7 +2062,7 @@ describe('webContents module', () => { it('can get multiple shared workers', async () => { const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); - const ready = emittedOnce(ipcMain, 'ready'); + const ready = once(ipcMain, 'ready'); w.loadFile(path.join(fixturesPath, 'api', 'shared-worker', 'shared-worker.html')); await ready; @@ -2075,17 +2076,17 @@ describe('webContents module', () => { it('can inspect a specific shared worker', async () => { const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); - const ready = emittedOnce(ipcMain, 'ready'); + const ready = once(ipcMain, 'ready'); w.loadFile(path.join(fixturesPath, 'api', 'shared-worker', 'shared-worker.html')); await ready; const sharedWorkers = w.webContents.getAllSharedWorkers(); - const devtoolsOpened = emittedOnce(w.webContents, 'devtools-opened'); + const devtoolsOpened = once(w.webContents, 'devtools-opened'); w.webContents.inspectSharedWorkerById(sharedWorkers[0].id); await devtoolsOpened; - const devtoolsClosed = emittedOnce(w.webContents, 'devtools-closed'); + const devtoolsClosed = once(w.webContents, 'devtools-closed'); w.webContents.closeDevTools(); await devtoolsClosed; }); @@ -2202,9 +2203,9 @@ describe('webContents module', () => { const bw = new BrowserWindow({ show: false }); await bw.loadURL('about:blank'); bw.webContents.executeJavaScript('child = window.open("", "", "show=no"); null'); - const [, child] = await emittedOnce(app, 'web-contents-created'); + const [, child] = await once(app, 'web-contents-created'); bw.webContents.executeJavaScript('child.document.title = "new title"'); - const [, title] = await emittedOnce(child, 'page-title-updated'); + const [, title] = await once(child, 'page-title-updated'); expect(title).to.equal('new title'); }); }); @@ -2212,7 +2213,7 @@ describe('webContents module', () => { describe('crashed event', () => { it('does not crash main process when destroying WebContents in it', async () => { const contents = (webContents as typeof ElectronInternal.WebContents).create({ nodeIntegration: true }); - const crashEvent = emittedOnce(contents, 'render-process-gone'); + const crashEvent = once(contents, 'render-process-gone'); await contents.loadURL('about:blank'); contents.forcefullyCrashRenderer(); await crashEvent; @@ -2226,7 +2227,7 @@ describe('webContents module', () => { const w = new BrowserWindow({ show: false }); await w.loadFile(path.join(fixturesPath, 'pages', 'base-page.html')); - const promise = emittedOnce(w.webContents, 'context-menu'); + const promise = once(w.webContents, 'context-menu'); // Simulate right-click to create context-menu event. const opts = { x: 0, y: 0, button: 'right' as any }; @@ -2247,7 +2248,7 @@ describe('webContents module', () => { it('closes when close() is called', async () => { const w = (webContents as typeof ElectronInternal.WebContents).create(); - const destroyed = emittedOnce(w, 'destroyed'); + const destroyed = once(w, 'destroyed'); w.close(); await destroyed; expect(w.isDestroyed()).to.be.true(); @@ -2256,7 +2257,7 @@ describe('webContents module', () => { it('closes when close() is called after loading a page', async () => { const w = (webContents as typeof ElectronInternal.WebContents).create(); await w.loadURL('about:blank'); - const destroyed = emittedOnce(w, 'destroyed'); + const destroyed = once(w, 'destroyed'); w.close(); await destroyed; expect(w.isDestroyed()).to.be.true(); @@ -2280,7 +2281,7 @@ describe('webContents module', () => { it('causes its parent browserwindow to be closed', async () => { const w = new BrowserWindow({ show: false }); await w.loadURL('about:blank'); - const closed = emittedOnce(w, 'closed'); + const closed = once(w, 'closed'); w.webContents.close(); await closed; expect(w.isDestroyed()).to.be.true(); @@ -2291,7 +2292,7 @@ describe('webContents module', () => { await w.loadURL('about:blank'); await w.executeJavaScript('window.onbeforeunload = () => "hello"; null'); w.on('will-prevent-unload', () => { throw new Error('unexpected will-prevent-unload'); }); - const destroyed = emittedOnce(w, 'destroyed'); + const destroyed = once(w, 'destroyed'); w.close(); await destroyed; expect(w.isDestroyed()).to.be.true(); @@ -2301,7 +2302,7 @@ describe('webContents module', () => { const w = (webContents as typeof ElectronInternal.WebContents).create(); await w.loadURL('about:blank'); await w.executeJavaScript('window.onbeforeunload = () => "hello"; null'); - const willPreventUnload = emittedOnce(w, 'will-prevent-unload'); + const willPreventUnload = once(w, 'will-prevent-unload'); w.close({ waitForBeforeUnload: true }); await willPreventUnload; expect(w.isDestroyed()).to.be.false(); @@ -2312,7 +2313,7 @@ describe('webContents module', () => { await w.loadURL('about:blank'); await w.executeJavaScript('window.onbeforeunload = () => "hello"; null'); w.once('will-prevent-unload', e => e.preventDefault()); - const destroyed = emittedOnce(w, 'destroyed'); + const destroyed = once(w, 'destroyed'); w.close({ waitForBeforeUnload: true }); await destroyed; expect(w.isDestroyed()).to.be.true(); @@ -2325,7 +2326,7 @@ describe('webContents module', () => { const w = new BrowserWindow({ show: false }); w.loadURL('about:blank'); w.webContents.executeJavaScript('window.moveTo(100, 100)', true); - const [, rect] = await emittedOnce(w.webContents, 'content-bounds-updated'); + const [, rect] = await once(w.webContents, 'content-bounds-updated'); const { width, height } = w.getBounds(); expect(rect).to.deep.equal({ x: 100, @@ -2342,7 +2343,7 @@ describe('webContents module', () => { const w = new BrowserWindow({ show: false }); w.loadURL('about:blank'); w.webContents.executeJavaScript('window.resizeTo(100, 100)', true); - const [, rect] = await emittedOnce(w.webContents, 'content-bounds-updated'); + const [, rect] = await once(w.webContents, 'content-bounds-updated'); const { x, y } = w.getBounds(); expect(rect).to.deep.equal({ x, diff --git a/spec/api-web-frame-main-spec.ts b/spec/api-web-frame-main-spec.ts index a5a4b1068bee..5c049ede3fb4 100644 --- a/spec/api-web-frame-main-spec.ts +++ b/spec/api-web-frame-main-spec.ts @@ -4,8 +4,10 @@ import * as path from 'path'; import * as url from 'url'; import { BrowserWindow, WebFrameMain, webFrameMain, ipcMain, app, WebContents } from 'electron/main'; import { closeAllWindows } from './lib/window-helpers'; -import { emittedOnce, emittedNTimes } from './lib/events-helpers'; -import { defer, delay, ifit, listen, waitUntil } from './lib/spec-helpers'; +import { emittedNTimes } from './lib/events-helpers'; +import { defer, ifit, listen, waitUntil } from './lib/spec-helpers'; +import { once } from 'events'; +import { setTimeout } from 'timers/promises'; describe('webFrameMain module', () => { const fixtures = path.resolve(__dirname, 'fixtures'); @@ -141,7 +143,7 @@ describe('webFrameMain module', () => { it('should show parent origin when child page is about:blank', async () => { const w = new BrowserWindow({ show: false }); await w.loadFile(path.join(fixtures, 'pages', 'blank.html')); - const webContentsCreated: Promise<[unknown, WebContents]> = emittedOnce(app, 'web-contents-created') as any; + const webContentsCreated: Promise<[unknown, WebContents]> = once(app, 'web-contents-created') as any; expect(w.webContents.mainFrame.origin).to.equal('file://'); await w.webContents.executeJavaScript('window.open("", null, "show=false"), null'); const [, childWebContents] = await webContentsCreated; @@ -161,7 +163,7 @@ describe('webFrameMain module', () => { expect(mainFrame.origin).to.equal(serverA.url.replace(/\/$/, '')); const [childFrame] = mainFrame.frames; expect(childFrame.origin).to.equal(serverB.url.replace(/\/$/, '')); - const webContentsCreated: Promise<[unknown, WebContents]> = emittedOnce(app, 'web-contents-created') as any; + const webContentsCreated: Promise<[unknown, WebContents]> = once(app, 'web-contents-created') as any; await childFrame.executeJavaScript('window.open("", null, "show=false"), null'); const [, childWebContents] = await webContentsCreated; expect(childWebContents.mainFrame.origin).to.equal(childFrame.origin); @@ -257,7 +259,7 @@ describe('webFrameMain module', () => { await webFrame.executeJavaScript('window.TEMP = 1', false); expect(webFrame.reload()).to.be.true(); - await emittedOnce(w.webContents, 'dom-ready'); + await once(w.webContents, 'dom-ready'); expect(await webFrame.executeJavaScript('window.TEMP', false)).to.be.null(); }); }); @@ -273,7 +275,7 @@ describe('webFrameMain module', () => { }); await w.loadURL('about:blank'); const webFrame = w.webContents.mainFrame; - const pongPromise = emittedOnce(ipcMain, 'preload-pong'); + const pongPromise = once(ipcMain, 'preload-pong'); webFrame.send('preload-ping'); const [, routingId] = await pongPromise; expect(routingId).to.equal(webFrame.routingId); @@ -293,7 +295,7 @@ describe('webFrameMain module', () => { const { mainFrame } = w.webContents; w.destroy(); // Wait for WebContents, and thus RenderFrameHost, to be destroyed. - await delay(); + await setTimeout(); expect(() => mainFrame.url).to.throw(); }); @@ -314,7 +316,7 @@ describe('webFrameMain module', () => { // Keep reference to mainFrame alive throughout crash and recovery. const { mainFrame } = w.webContents; await w.webContents.loadURL(server.url); - const crashEvent = emittedOnce(w.webContents, 'render-process-gone'); + const crashEvent = once(w.webContents, 'render-process-gone'); w.webContents.forcefullyCrashRenderer(); await crashEvent; await w.webContents.loadURL(server.url); @@ -330,11 +332,11 @@ describe('webFrameMain module', () => { // Keep reference to mainFrame alive throughout crash and recovery. const { mainFrame } = w.webContents; await w.webContents.loadURL(server.url); - const crashEvent = emittedOnce(w.webContents, 'render-process-gone'); + const crashEvent = once(w.webContents, 'render-process-gone'); w.webContents.forcefullyCrashRenderer(); await crashEvent; // A short wait seems to be required to reproduce the crash. - await delay(100); + await setTimeout(100); await w.webContents.loadURL(crossOriginUrl); // Log just to keep mainFrame in scope. console.log('mainFrame.url', mainFrame.url); @@ -366,7 +368,7 @@ describe('webFrameMain module', () => { describe('"frame-created" event', () => { it('emits when the main frame is created', async () => { const w = new BrowserWindow({ show: false }); - const promise = emittedOnce(w.webContents, 'frame-created'); + const promise = once(w.webContents, 'frame-created'); w.webContents.loadFile(path.join(subframesPath, 'frame.html')); const [, details] = await promise; expect(details.frame).to.equal(w.webContents.mainFrame); @@ -406,7 +408,7 @@ describe('webFrameMain module', () => { describe('"dom-ready" event', () => { it('emits for top-level frame', async () => { const w = new BrowserWindow({ show: false }); - const promise = emittedOnce(w.webContents.mainFrame, 'dom-ready'); + const promise = once(w.webContents.mainFrame, 'dom-ready'); w.webContents.loadURL('about:blank'); await promise; }); diff --git a/spec/api-web-frame-spec.ts b/spec/api-web-frame-spec.ts index 3e218204afcb..d2b007aee0bd 100644 --- a/spec/api-web-frame-spec.ts +++ b/spec/api-web-frame-spec.ts @@ -1,8 +1,8 @@ import { expect } from 'chai'; import * as path from 'path'; import { BrowserWindow, ipcMain, WebContents } from 'electron/main'; -import { emittedOnce } from './lib/events-helpers'; import { defer } from './lib/spec-helpers'; +import { once } from 'events'; describe('webFrame module', () => { const fixtures = path.resolve(__dirname, 'fixtures'); @@ -17,7 +17,7 @@ describe('webFrame module', () => { } }); defer(() => w.close()); - const isSafe = emittedOnce(ipcMain, 'executejs-safe'); + const isSafe = once(ipcMain, 'executejs-safe'); w.loadURL('about:blank'); const [, wasSafe] = await isSafe; expect(wasSafe).to.equal(true); @@ -33,7 +33,7 @@ describe('webFrame module', () => { } }); defer(() => w.close()); - const execError = emittedOnce(ipcMain, 'executejs-safe'); + const execError = once(ipcMain, 'executejs-safe'); w.loadURL('about:blank'); const [, error] = await execError; expect(error).to.not.equal(null, 'Error should not be null'); diff --git a/spec/api-web-request-spec.ts b/spec/api-web-request-spec.ts index dccdfeff5e60..54341559e7a7 100644 --- a/spec/api-web-request-spec.ts +++ b/spec/api-web-request-spec.ts @@ -6,8 +6,8 @@ import * as url from 'url'; import * as WebSocket from 'ws'; import { ipcMain, protocol, session, WebContents, webContents } from 'electron/main'; import { Socket } from 'net'; -import { emittedOnce } from './lib/events-helpers'; import { listen } from './lib/spec-helpers'; +import { once } from 'events'; const fixturesPath = path.resolve(__dirname, 'fixtures'); @@ -545,7 +545,7 @@ describe('webRequest module', () => { }); contents.loadFile(path.join(fixturesPath, 'api', 'webrequest.html'), { query: { port: `${port}` } }); - await emittedOnce(ipcMain, 'websocket-success'); + await once(ipcMain, 'websocket-success'); expect(receivedHeaders['/websocket'].Upgrade[0]).to.equal('websocket'); expect(receivedHeaders['/'].foo1[0]).to.equal('bar1'); diff --git a/spec/asar-spec.ts b/spec/asar-spec.ts index 827b1d612fe3..eded87919d5a 100644 --- a/spec/asar-spec.ts +++ b/spec/asar-spec.ts @@ -4,9 +4,9 @@ import * as url from 'url'; import { Worker } from 'worker_threads'; import { BrowserWindow, ipcMain } from 'electron/main'; import { closeAllWindows } from './lib/window-helpers'; -import { emittedOnce } from './lib/events-helpers'; import { getRemoteContext, ifdescribe, itremote, useRemoteContext } from './lib/spec-helpers'; import * as importedFs from 'fs'; +import { once } from 'events'; const features = process._linkedBinding('electron_common_features'); @@ -32,7 +32,7 @@ describe('asar package', () => { } }); const p = path.resolve(asarDir, 'web.asar', 'index.html'); - const dirnameEvent = emittedOnce(ipcMain, 'dirname'); + const dirnameEvent = once(ipcMain, 'dirname'); w.loadFile(p); const [, dirname] = await dirnameEvent; expect(dirname).to.equal(path.dirname(p)); @@ -53,7 +53,7 @@ describe('asar package', () => { } }); const p = path.resolve(asarDir, 'script.asar', 'index.html'); - const ping = emittedOnce(ipcMain, 'ping'); + const ping = once(ipcMain, 'ping'); w.loadFile(p); const [, message] = await ping; expect(message).to.equal('pong'); @@ -77,7 +77,7 @@ describe('asar package', () => { }); const p = path.resolve(asarDir, 'video.asar', 'index.html'); w.loadFile(p); - const [, message, error] = await emittedOnce(ipcMain, 'asar-video'); + const [, message, error] = await once(ipcMain, 'asar-video'); if (message === 'ended') { expect(error).to.be.null(); } else if (message === 'error') { @@ -1514,7 +1514,7 @@ describe('asar package', function () { /* ifit(features.isRunAsNodeEnabled())('is available in forked scripts', async function () { const child = ChildProcess.fork(path.join(fixtures, 'module', 'original-fs.js')); - const message = emittedOnce(child, 'message'); + const message = once(child, 'message'); child.send('message'); const [msg] = await message; expect(msg).to.equal('object'); diff --git a/spec/autofill-spec.ts b/spec/autofill-spec.ts index ebe57ba44689..fac4e67ee3b3 100644 --- a/spec/autofill-spec.ts +++ b/spec/autofill-spec.ts @@ -1,8 +1,8 @@ import { BrowserWindow } from 'electron'; import * as path from 'path'; -import { delay } from './lib/spec-helpers'; import { expect } from 'chai'; import { closeAllWindows } from './lib/window-helpers'; +import { setTimeout } from 'timers/promises'; const fixturesPath = path.resolve(__dirname, 'fixtures'); @@ -17,7 +17,7 @@ describe('autofill', () => { const inputText = 'clap'; for (const keyCode of inputText) { w.webContents.sendInputEvent({ type: 'char', keyCode }); - await delay(100); + await setTimeout(100); } w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Down' }); @@ -36,7 +36,7 @@ describe('autofill', () => { w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Tab' }); w.webContents.sendInputEvent({ type: 'keyDown', keyCode }); w.webContents.sendInputEvent({ type: 'char', keyCode }); - await delay(100); + await setTimeout(100); } w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Tab' }); diff --git a/spec/chromium-spec.ts b/spec/chromium-spec.ts index eaf96d4ba6fd..cc3fa6a3ac82 100644 --- a/spec/chromium-spec.ts +++ b/spec/chromium-spec.ts @@ -1,6 +1,5 @@ import { expect } from 'chai'; import { BrowserWindow, WebContents, webFrameMain, session, ipcMain, app, protocol, webContents } from 'electron/main'; -import { emittedOnce } from './lib/events-helpers'; import { closeAllWindows } from './lib/window-helpers'; import * as https from 'https'; import * as http from 'http'; @@ -8,11 +7,12 @@ import * as path from 'path'; import * as fs from 'fs'; import * as url from 'url'; import * as ChildProcess from 'child_process'; -import { EventEmitter } from 'events'; +import { EventEmitter, once } from 'events'; import { promisify } from 'util'; -import { ifit, ifdescribe, defer, delay, itremote, listen } from './lib/spec-helpers'; +import { ifit, ifdescribe, defer, itremote, listen } from './lib/spec-helpers'; import { PipeTransport } from './pipe-transport'; import * as ws from 'ws'; +import { setTimeout } from 'timers/promises'; const features = process._linkedBinding('electron_common_features'); @@ -64,7 +64,7 @@ describe('reporting api', () => { show: false }); try { - const reportGenerated = emittedOnce(reports, 'report'); + const reportGenerated = once(reports, 'report'); await bw.loadURL(url); const [report] = await reportGenerated; expect(report).to.be.an('array'); @@ -86,7 +86,7 @@ describe('window.postMessage', () => { it('sets the source and origin correctly', async () => { const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, contextIsolation: false } }); w.loadURL(`file://${fixturesPath}/pages/window-open-postMessage-driver.html`); - const [, message] = await emittedOnce(ipcMain, 'complete'); + const [, message] = await once(ipcMain, 'complete'); expect(message.data).to.equal('testing'); expect(message.origin).to.equal('file://'); expect(message.sourceEqualsOpener).to.equal(true); @@ -108,11 +108,11 @@ describe('focus handling', () => { } }); - const webviewReady = emittedOnce(w.webContents, 'did-attach-webview'); + const webviewReady = once(w.webContents, 'did-attach-webview'); await w.loadFile(path.join(fixturesPath, 'pages', 'tab-focus-loop-elements.html')); const [, wvContents] = await webviewReady; webviewContents = wvContents; - await emittedOnce(webviewContents, 'did-finish-load'); + await once(webviewContents, 'did-finish-load'); w.focus(); }); @@ -123,7 +123,7 @@ describe('focus handling', () => { }); const expectFocusChange = async () => { - const [, focusedElementId] = await emittedOnce(ipcMain, 'focus-changed'); + const [, focusedElementId] = await once(ipcMain, 'focus-changed'); return focusedElementId; }; @@ -224,7 +224,7 @@ describe('web security', () => { it('engages CORB when web security is not disabled', async () => { const w = new BrowserWindow({ show: false, webPreferences: { webSecurity: true, nodeIntegration: true, contextIsolation: false } }); - const p = emittedOnce(ipcMain, 'success'); + const p = once(ipcMain, 'success'); await w.loadURL(`data:text/html,`); - const [,, message] = await emittedOnce(w.webContents, 'console-message'); + const [,, message] = await once(w.webContents, 'console-message'); expect(message).to.equal('success'); }); }); @@ -410,7 +410,7 @@ describe('command line switches', () => { let stderr = ''; appProcess.stderr.on('data', (data) => { stderr += data; }); - const [code, signal] = await emittedOnce(appProcess, 'exit'); + const [code, signal] = await once(appProcess, 'exit'); if (code !== 0) { throw new Error(`Process exited with code "${code}" signal "${signal}" output "${output}" stderr "${stderr}"`); } @@ -471,7 +471,7 @@ describe('command line switches', () => { const stdio = appProcess.stdio as unknown as [NodeJS.ReadableStream, NodeJS.WritableStream, NodeJS.WritableStream, NodeJS.WritableStream, NodeJS.ReadableStream]; const pipe = new PipeTransport(stdio[3], stdio[4]); pipe.send({ id: 1, method: 'Browser.close', params: {} }); - await emittedOnce(appProcess, 'exit'); + await once(appProcess, 'exit'); }); }); @@ -533,7 +533,7 @@ describe('chromium features', () => { let output = ''; fpsProcess.stdout.on('data', data => { output += data; }); - await emittedOnce(fpsProcess, 'exit'); + await once(fpsProcess, 'exit'); expect(output).to.include(fps.join(',')); }); @@ -545,7 +545,7 @@ describe('chromium features', () => { let output = ''; fpsProcess.stdout.on('data', data => { output += data; }); - await emittedOnce(fpsProcess, 'exit'); + await once(fpsProcess, 'exit'); expect(output).to.include(fps.join(',')); }); @@ -711,7 +711,7 @@ describe('chromium features', () => { contextIsolation: false } }); - const message = emittedOnce(w.webContents, 'ipc-message'); + const message = once(w.webContents, 'ipc-message'); w.webContents.session.setPermissionRequestHandler((wc, permission, callback) => { if (permission === 'geolocation') { callback(false); @@ -751,7 +751,7 @@ describe('chromium features', () => { appProcess = ChildProcess.spawn(process.execPath, [appPath]); - const [code] = await emittedOnce(appProcess, 'exit'); + const [code] = await once(appProcess, 'exit'); expect(code).to.equal(0); }); @@ -781,7 +781,7 @@ describe('chromium features', () => { it('Worker has node integration with nodeIntegrationInWorker', async () => { const w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true, nodeIntegrationInWorker: true, contextIsolation: false } }); w.loadURL(`file://${fixturesPath}/pages/worker.html`); - const [, data] = await emittedOnce(ipcMain, 'worker-result'); + const [, data] = await once(ipcMain, 'worker-result'); expect(data).to.equal('object function object function'); }); @@ -863,7 +863,7 @@ describe('chromium features', () => { await w.loadFile(path.join(fixturesPath, 'pages', 'form-with-data.html')); - const loadPromise = emittedOnce(w.webContents, 'did-finish-load'); + const loadPromise = once(w.webContents, 'did-finish-load'); w.webContents.executeJavaScript(` const form = document.querySelector('form') @@ -887,7 +887,7 @@ describe('chromium features', () => { await w.loadFile(path.join(fixturesPath, 'pages', 'form-with-data.html')); - const windowCreatedPromise = emittedOnce(app, 'browser-window-created'); + const windowCreatedPromise = once(app, 'browser-window-created'); w.webContents.executeJavaScript(` const form = document.querySelector('form') @@ -919,7 +919,7 @@ describe('chromium features', () => { defer(() => { w.close(); }); - const promise = emittedOnce(app, 'browser-window-created'); + const promise = once(app, 'browser-window-created'); w.loadFile(path.join(fixturesPath, 'pages', 'window-open.html')); const [, newWindow] = await promise; expect(newWindow.isVisible()).to.equal(true); @@ -955,7 +955,7 @@ describe('chromium features', () => { w.webContents.executeJavaScript(` { b = window.open('devtools://devtools/bundled/inspector.html', '', 'nodeIntegration=no,show=no'); null } `); - const [, contents] = await emittedOnce(app, 'web-contents-created'); + const [, contents] = await once(app, 'web-contents-created'); const typeofProcessGlobal = await contents.executeJavaScript('typeof process'); expect(typeofProcessGlobal).to.equal('undefined'); }); @@ -966,7 +966,7 @@ describe('chromium features', () => { w.webContents.executeJavaScript(` { b = window.open('about:blank', '', 'nodeIntegration=no,show=no'); null } `); - const [, contents] = await emittedOnce(app, 'web-contents-created'); + const [, contents] = await once(app, 'web-contents-created'); const typeofProcessGlobal = await contents.executeJavaScript('typeof process'); expect(typeofProcessGlobal).to.equal('undefined'); }); @@ -983,12 +983,12 @@ describe('chromium features', () => { w.webContents.executeJavaScript(` { b = window.open(${JSON.stringify(windowUrl)}, '', 'javascript=no,show=no'); null } `); - const [, contents] = await emittedOnce(app, 'web-contents-created'); - await emittedOnce(contents, 'did-finish-load'); + const [, contents] = await once(app, 'web-contents-created'); + await once(contents, 'did-finish-load'); // 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 [, window] = await emittedOnce(app, 'browser-window-created'); + const [, window] = await once(app, 'browser-window-created'); const preferences = window.webContents.getLastWebPreferences(); expect(preferences.javascript).to.be.false(); }); @@ -1003,8 +1003,8 @@ describe('chromium features', () => { const w = new BrowserWindow({ show: false }); w.webContents.loadFile(path.resolve(__dirname, 'fixtures', 'blank.html')); w.webContents.executeJavaScript(`{ b = window.open(${JSON.stringify(targetURL)}); null }`); - const [, window] = await emittedOnce(app, 'browser-window-created'); - await emittedOnce(window.webContents, 'did-finish-load'); + const [, window] = await once(app, 'browser-window-created'); + await once(window.webContents, 'did-finish-load'); expect(await w.webContents.executeJavaScript('b.location.href')).to.equal(targetURL); }); @@ -1012,30 +1012,30 @@ describe('chromium features', () => { const w = new BrowserWindow({ show: false }); w.webContents.loadFile(path.resolve(__dirname, 'fixtures', 'blank.html')); w.webContents.executeJavaScript('{ b = window.open("about:blank"); null }'); - const [, { webContents }] = await emittedOnce(app, 'browser-window-created'); - await emittedOnce(webContents, 'did-finish-load'); + const [, { webContents }] = await once(app, 'browser-window-created'); + await once(webContents, 'did-finish-load'); // When it loads, redirect w.webContents.executeJavaScript(`{ b.location = ${JSON.stringify(`file://${fixturesPath}/pages/base-page.html`)}; null }`); - await emittedOnce(webContents, 'did-finish-load'); + await once(webContents, 'did-finish-load'); }); it('defines a window.location.href setter', async () => { const w = new BrowserWindow({ show: false }); w.webContents.loadFile(path.resolve(__dirname, 'fixtures', 'blank.html')); w.webContents.executeJavaScript('{ b = window.open("about:blank"); null }'); - const [, { webContents }] = await emittedOnce(app, 'browser-window-created'); - await emittedOnce(webContents, 'did-finish-load'); + const [, { webContents }] = await once(app, 'browser-window-created'); + await once(webContents, 'did-finish-load'); // When it loads, redirect w.webContents.executeJavaScript(`{ b.location.href = ${JSON.stringify(`file://${fixturesPath}/pages/base-page.html`)}; null }`); - await emittedOnce(webContents, 'did-finish-load'); + await once(webContents, 'did-finish-load'); }); it('open a blank page when no URL is specified', async () => { const w = new BrowserWindow({ show: false }); w.loadURL('about:blank'); w.webContents.executeJavaScript('{ b = window.open(); null }'); - const [, { webContents }] = await emittedOnce(app, 'browser-window-created'); - await emittedOnce(webContents, 'did-finish-load'); + const [, { webContents }] = await once(app, 'browser-window-created'); + await once(webContents, 'did-finish-load'); expect(await w.webContents.executeJavaScript('b.location.href')).to.equal('about:blank'); }); @@ -1043,8 +1043,8 @@ describe('chromium features', () => { const w = new BrowserWindow({ show: false }); w.loadURL('about:blank'); w.webContents.executeJavaScript('{ b = window.open(\'\'); null }'); - const [, { webContents }] = await emittedOnce(app, 'browser-window-created'); - await emittedOnce(webContents, 'did-finish-load'); + const [, { webContents }] = await once(app, 'browser-window-created'); + await once(webContents, 'did-finish-load'); expect(await w.webContents.executeJavaScript('b.location.href')).to.equal('about:blank'); }); @@ -1145,7 +1145,7 @@ describe('chromium features', () => { } }); w.loadFile(path.join(fixturesPath, 'pages', 'window-opener.html')); - const [, channel, opener] = await emittedOnce(w.webContents, 'ipc-message'); + const [, channel, opener] = await once(w.webContents, 'ipc-message'); expect(channel).to.equal('opener'); expect(opener).to.equal(null); }); @@ -1267,8 +1267,9 @@ describe('chromium features', () => { } }); w.loadFile(path.join(fixturesPath, 'pages', 'media-id-reset.html')); - const [, firstDeviceIds] = await emittedOnce(ipcMain, 'deviceIds'); - const [, secondDeviceIds] = await emittedOnce(ipcMain, 'deviceIds', () => w.webContents.reload()); + const [, firstDeviceIds] = await once(ipcMain, 'deviceIds'); + w.webContents.reload(); + const [, secondDeviceIds] = await once(ipcMain, 'deviceIds'); expect(firstDeviceIds).to.deep.equal(secondDeviceIds); }); @@ -1283,9 +1284,10 @@ describe('chromium features', () => { } }); w.loadFile(path.join(fixturesPath, 'pages', 'media-id-reset.html')); - const [, firstDeviceIds] = await emittedOnce(ipcMain, 'deviceIds'); + const [, firstDeviceIds] = await once(ipcMain, 'deviceIds'); await ses.clearStorageData({ storages: ['cookies'] }); - const [, secondDeviceIds] = await emittedOnce(ipcMain, 'deviceIds', () => w.webContents.reload()); + w.webContents.reload(); + const [, secondDeviceIds] = await once(ipcMain, 'deviceIds'); expect(firstDeviceIds).to.not.deep.equal(secondDeviceIds); }); @@ -1477,35 +1479,35 @@ describe('chromium features', () => { }); it('cannot access localStorage', async () => { - const response = emittedOnce(ipcMain, 'local-storage-response'); + const response = once(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', async () => { - const response = emittedOnce(ipcMain, 'session-storage-response'); + const response = once(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', async () => { - const response = emittedOnce(ipcMain, 'web-sql-response'); + const response = once(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', async () => { - const response = emittedOnce(ipcMain, 'indexed-db-response'); + const response = once(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', async () => { - const response = emittedOnce(ipcMain, 'cookie-response'); + const response = once(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.'); @@ -1529,7 +1531,7 @@ describe('chromium features', () => { res.end(); } }; - setTimeout(respond, 0); + setTimeout().then(respond); }); serverUrl = (await listen(server)).url; serverCrossSiteUrl = serverUrl.replace('127.0.0.1', 'localhost'); @@ -1599,7 +1601,7 @@ describe('chromium features', () => { contextIsolation: false }); contents.loadURL(origin); - const [, error] = await emittedOnce(ipcMain, 'web-sql-response'); + const [, error] = await once(ipcMain, 'web-sql-response'); expect(error).to.be.null(); }); @@ -1611,7 +1613,7 @@ describe('chromium features', () => { contextIsolation: false }); contents.loadURL(origin); - const [, error] = await emittedOnce(ipcMain, 'web-sql-response'); + const [, error] = await once(ipcMain, 'web-sql-response'); expect(error).to.equal(securityError); }); @@ -1623,7 +1625,7 @@ describe('chromium features', () => { contextIsolation: false }); contents.loadURL(origin); - const [, error] = await emittedOnce(ipcMain, 'web-sql-response'); + const [, error] = await once(ipcMain, 'web-sql-response'); expect(error).to.equal(securityError); const dbName = 'random'; const result = await contents.executeJavaScript(` @@ -1654,9 +1656,9 @@ describe('chromium features', () => { } }); w.webContents.loadURL(origin); - const [, error] = await emittedOnce(ipcMain, 'web-sql-response'); + const [, error] = await once(ipcMain, 'web-sql-response'); expect(error).to.be.null(); - const webviewResult = emittedOnce(ipcMain, 'web-sql-response'); + const webviewResult = once(ipcMain, 'web-sql-response'); await w.webContents.executeJavaScript(` new Promise((resolve, reject) => { const webview = new WebView(); @@ -1684,7 +1686,7 @@ describe('chromium features', () => { } }); w.webContents.loadURL('data:text/html,'); - const webviewResult = emittedOnce(ipcMain, 'web-sql-response'); + const webviewResult = once(ipcMain, 'web-sql-response'); await w.webContents.executeJavaScript(` new Promise((resolve, reject) => { const webview = new WebView(); @@ -1711,9 +1713,9 @@ describe('chromium features', () => { } }); w.webContents.loadURL(origin); - const [, error] = await emittedOnce(ipcMain, 'web-sql-response'); + const [, error] = await once(ipcMain, 'web-sql-response'); expect(error).to.be.null(); - const webviewResult = emittedOnce(ipcMain, 'web-sql-response'); + const webviewResult = once(ipcMain, 'web-sql-response'); await w.webContents.executeJavaScript(` new Promise((resolve, reject) => { const webview = new WebView(); @@ -1753,7 +1755,7 @@ describe('chromium features', () => { // 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. - await delay(1); + await setTimeout(1); try { const storedLength = await w.webContents.executeJavaScript(`${storageName}.getItem(${JSON.stringify(testKeyName)}).length`); expect(storedLength).to.equal(length); @@ -1803,22 +1805,22 @@ describe('chromium features', () => { const w = new BrowserWindow({ show: false }); w.loadURL(pdfSource); - await emittedOnce(w.webContents, 'did-finish-load'); + await once(w.webContents, 'did-finish-load'); }); it('opens when loading a pdf resource as top level navigation', async () => { const w = new BrowserWindow({ show: false }); w.loadURL(pdfSource); - const [, contents] = await emittedOnce(app, 'web-contents-created'); - await emittedOnce(contents, 'did-navigate'); + const [, contents] = await once(app, 'web-contents-created'); + await once(contents, 'did-navigate'); expect(contents.getURL()).to.equal('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html'); }); it('opens when loading a pdf resource in a iframe', async () => { const w = new BrowserWindow({ show: false }); w.loadFile(path.join(__dirname, 'fixtures', 'pages', 'pdf-in-iframe.html')); - const [, contents] = await emittedOnce(app, 'web-contents-created'); - await emittedOnce(contents, 'did-navigate'); + const [, contents] = await once(app, 'web-contents-created'); + await once(contents, 'did-navigate'); expect(contents.getURL()).to.equal('chrome-extension://mhjfbmdgcfjbbpaeojofohoefgiehjai/index.html'); }); }); @@ -1831,7 +1833,7 @@ describe('chromium features', () => { // History should have current page by now. expect((w.webContents as any).length()).to.equal(1); - const waitCommit = emittedOnce(w.webContents, 'navigation-entry-committed'); + const waitCommit = once(w.webContents, 'navigation-entry-committed'); w.webContents.executeJavaScript('window.history.pushState({}, "")'); await waitCommit; // Initial page + pushed state. @@ -1844,15 +1846,15 @@ describe('chromium features', () => { const w = new BrowserWindow({ show: false }); w.loadURL('data:text/html,'); await Promise.all([ - emittedOnce(w.webContents, 'navigation-entry-committed'), - emittedOnce(w.webContents, 'did-frame-navigate'), - emittedOnce(w.webContents, 'did-navigate') + once(w.webContents, 'navigation-entry-committed'), + once(w.webContents, 'did-frame-navigate'), + once(w.webContents, 'did-navigate') ]); w.webContents.executeJavaScript('window.history.pushState(1, "")'); await Promise.all([ - emittedOnce(w.webContents, 'navigation-entry-committed'), - emittedOnce(w.webContents, 'did-navigate-in-page') + once(w.webContents, 'navigation-entry-committed'), + once(w.webContents, 'did-navigate-in-page') ]); (w.webContents as any).once('navigation-entry-committed', () => { @@ -2287,7 +2289,7 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () }); ifit(process.platform !== 'darwin')('can fullscreen from out-of-process iframes (non-macOS)', async () => { - const fullscreenChange = emittedOnce(ipcMain, 'fullscreenChange'); + const fullscreenChange = once(ipcMain, 'fullscreenChange'); const html = ``; w.loadURL(`data:text/html,${html}`); @@ -2302,7 +2304,7 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () "document.querySelector('iframe').contentWindow.postMessage('exitFullscreen', '*')" ); - await delay(500); + await setTimeout(500); const width = await w.webContents.executeJavaScript( "document.querySelector('iframe').offsetWidth" @@ -2311,8 +2313,8 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () }); ifit(process.platform === 'darwin')('can fullscreen from out-of-process iframes (macOS)', async () => { - await emittedOnce(w, 'enter-full-screen'); - const fullscreenChange = emittedOnce(ipcMain, 'fullscreenChange'); + await once(w, 'enter-full-screen'); + const fullscreenChange = once(ipcMain, 'fullscreenChange'); const html = ``; w.loadURL(`data:text/html,${html}`); @@ -2326,7 +2328,7 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () await w.webContents.executeJavaScript( "document.querySelector('iframe').contentWindow.postMessage('exitFullscreen', '*')" ); - await emittedOnce(w.webContents, 'leave-html-full-screen'); + await once(w.webContents, 'leave-html-full-screen'); const width = await w.webContents.executeJavaScript( "document.querySelector('iframe').offsetWidth" @@ -2334,14 +2336,14 @@ describe('iframe using HTML fullscreen API while window is OS-fullscreened', () expect(width).to.equal(0); w.setFullScreen(false); - await emittedOnce(w, 'leave-full-screen'); + await once(w, 'leave-full-screen'); }); // TODO(jkleinsc) fix this flaky test on WOA ifit(process.platform !== 'win32' || process.arch !== 'arm64')('can fullscreen from in-process iframes', async () => { - if (process.platform === 'darwin') await emittedOnce(w, 'enter-full-screen'); + if (process.platform === 'darwin') await once(w, 'enter-full-screen'); - const fullscreenChange = emittedOnce(ipcMain, 'fullscreenChange'); + const fullscreenChange = once(ipcMain, 'fullscreenChange'); w.loadFile(path.join(fixturesPath, 'pages', 'fullscreen-ipif.html')); await fullscreenChange; @@ -2644,7 +2646,7 @@ ifdescribe((process.platform !== 'linux' || app.isUnityRunning()))('navigator.se async function waitForBadgeCount (value: number) { let badgeCount = app.getBadgeCount(); while (badgeCount !== value) { - await delay(10); + await setTimeout(10); badgeCount = app.getBadgeCount(); } return badgeCount; @@ -2839,7 +2841,7 @@ describe('navigator.hid', () => { const grantedDevices = await w.webContents.executeJavaScript('navigator.hid.getDevices()'); expect(grantedDevices).to.not.be.empty(); w.loadURL(serverUrl); - const [,,,,, frameProcessId, frameRoutingId] = await emittedOnce(w.webContents, 'did-frame-navigate'); + const [,,,,, frameProcessId, frameRoutingId] = await once(w.webContents, 'did-frame-navigate'); const frame = webFrameMain.fromId(frameProcessId, frameRoutingId); expect(!!frame).to.be.true(); if (frame) { @@ -3039,7 +3041,7 @@ describe('navigator.usb', () => { const grantedDevices = await w.webContents.executeJavaScript('navigator.usb.getDevices()'); expect(grantedDevices).to.not.be.empty(); w.loadURL(serverUrl); - const [,,,,, frameProcessId, frameRoutingId] = await emittedOnce(w.webContents, 'did-frame-navigate'); + const [,,,,, frameProcessId, frameRoutingId] = await once(w.webContents, 'did-frame-navigate'); const frame = webFrameMain.fromId(frameProcessId, frameRoutingId); expect(!!frame).to.be.true(); if (frame) { diff --git a/spec/extensions-spec.ts b/spec/extensions-spec.ts index 1aa083de440d..f76e5a43bf77 100644 --- a/spec/extensions-spec.ts +++ b/spec/extensions-spec.ts @@ -5,8 +5,9 @@ import * as http from 'http'; import * as path from 'path'; import * as fs from 'fs'; import * as WebSocket from 'ws'; -import { emittedOnce, emittedNTimes, emittedUntil } from './lib/events-helpers'; +import { emittedNTimes, emittedUntil } from './lib/events-helpers'; import { ifit, listen } from './lib/spec-helpers'; +import { once } from 'events'; const uuid = require('uuid'); @@ -53,7 +54,7 @@ describe('chrome extensions', () => { const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); await w.loadURL('about:blank'); - const promise = emittedOnce(app, 'web-contents-created'); + const promise = once(app, 'web-contents-created'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page')); const args: any = await promise; const wc: Electron.WebContents = args[1]; @@ -73,7 +74,7 @@ describe('chrome extensions', () => { const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, sandbox: true } }); await w.loadURL('about:blank'); - const promise = emittedOnce(app, 'web-contents-created'); + const promise = once(app, 'web-contents-created'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page')); const args: any = await promise; const wc: Electron.WebContents = args[1]; @@ -151,7 +152,7 @@ describe('chrome extensions', () => { it('emits extension lifecycle events', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); - const loadedPromise = emittedOnce(customSession, 'extension-loaded'); + const loadedPromise = once(customSession, 'extension-loaded'); const extension = await customSession.loadExtension(path.join(fixtures, 'extensions', 'red-bg')); const [, loadedExtension] = await loadedPromise; const [, readyExtension] = await emittedUntil(customSession, 'extension-ready', (event: Event, extension: Extension) => { @@ -161,7 +162,7 @@ describe('chrome extensions', () => { expect(loadedExtension).to.deep.equal(extension); expect(readyExtension).to.deep.equal(extension); - const unloadedPromise = emittedOnce(customSession, 'extension-unloaded'); + const unloadedPromise = once(customSession, 'extension-unloaded'); await customSession.removeExtension(extension.id); const [, unloadedExtension] = await unloadedPromise; expect(unloadedExtension).to.deep.equal(extension); @@ -199,7 +200,7 @@ describe('chrome extensions', () => { let w: BrowserWindow; let extension: Extension; const exec = async (name: string) => { - const p = emittedOnce(ipcMain, 'success'); + const p = once(ipcMain, 'success'); await w.webContents.executeJavaScript(`exec('${name}')`); const [, result] = await p; return result; @@ -224,7 +225,7 @@ describe('chrome extensions', () => { describe('chrome.runtime', () => { let w: BrowserWindow; const exec = async (name: string) => { - const p = emittedOnce(ipcMain, 'success'); + const p = once(ipcMain, 'success'); await w.webContents.executeJavaScript(`exec('${name}')`); const [, result] = await p; return result; @@ -262,7 +263,7 @@ describe('chrome extensions', () => { await customSession.loadExtension(path.join(fixtures, 'extensions', 'chrome-storage')); const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true, contextIsolation: false } }); try { - const p = emittedOnce(ipcMain, 'storage-success'); + const p = once(ipcMain, 'storage-success'); await w.loadURL(url); const [, v] = await p; expect(v).to.equal('value'); @@ -352,7 +353,7 @@ describe('chrome extensions', () => { const message = { method: 'executeScript', args: ['1 + 2'] }; w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, responseString] = await emittedOnce(w.webContents, 'console-message'); + const [,, responseString] = await once(w.webContents, 'console-message'); const response = JSON.parse(responseString); expect(response).to.equal(3); @@ -366,7 +367,7 @@ describe('chrome extensions', () => { const message = { method: 'connectTab', args: [portName] }; w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, responseString] = await emittedOnce(w.webContents, 'console-message'); + const [,, responseString] = await once(w.webContents, 'console-message'); const response = responseString.split(','); expect(response[0]).to.equal(portName); expect(response[1]).to.equal('howdy'); @@ -379,7 +380,7 @@ describe('chrome extensions', () => { const message = { method: 'sendMessage', args: ['Hello World!'] }; w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, responseString] = await emittedOnce(w.webContents, 'console-message'); + const [,, responseString] = await once(w.webContents, 'console-message'); const response = JSON.parse(responseString); expect(response.message).to.equal('Hello World!'); @@ -393,12 +394,12 @@ describe('chrome extensions', () => { const w2 = new BrowserWindow({ show: false, webPreferences: { session: customSession } }); await w2.loadURL('about:blank'); - const w2Navigated = emittedOnce(w2.webContents, 'did-navigate'); + const w2Navigated = once(w2.webContents, 'did-navigate'); const message = { method: 'update', args: [w2.webContents.id, { url }] }; w.webContents.executeJavaScript(`window.postMessage('${JSON.stringify(message)}', '*')`); - const [,, responseString] = await emittedOnce(w.webContents, 'console-message'); + const [,, responseString] = await once(w.webContents, 'console-message'); const response = JSON.parse(responseString); await w2Navigated; @@ -416,7 +417,7 @@ describe('chrome extensions', () => { const w = new BrowserWindow({ show: false, webPreferences: { session: customSession, nodeIntegration: true, contextIsolation: false } }); try { w.loadURL(url); - const [, resp] = await emittedOnce(ipcMain, 'bg-page-message-response'); + const [, resp] = await once(ipcMain, 'bg-page-message-response'); expect(resp.message).to.deep.equal({ some: 'message' }); expect(resp.sender.id).to.be.a('string'); expect(resp.sender.origin).to.equal(url); @@ -455,18 +456,18 @@ describe('chrome extensions', () => { it('has session in background page', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); - const promise = emittedOnce(app, 'web-contents-created'); + const promise = once(app, 'web-contents-created'); const { id } = await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page')); const [, bgPageContents] = await promise; expect(bgPageContents.getType()).to.equal('backgroundPage'); - await emittedOnce(bgPageContents, 'did-finish-load'); + await once(bgPageContents, 'did-finish-load'); expect(bgPageContents.getURL()).to.equal(`chrome-extension://${id}/_generated_background_page.html`); expect(bgPageContents.session).to.not.equal(undefined); }); it('can open devtools of background page', async () => { const customSession = session.fromPartition(`persist:${require('uuid').v4()}`); - const promise = emittedOnce(app, 'web-contents-created'); + const promise = once(app, 'web-contents-created'); await customSession.loadExtension(path.join(fixtures, 'extensions', 'persistent-background-page')); const [, bgPageContents] = await promise; expect(bgPageContents.getType()).to.equal('backgroundPage'); @@ -508,7 +509,7 @@ describe('chrome extensions', () => { ifit(process.platform !== 'win32' || process.arch !== 'arm64')('loads a devtools extension', async () => { const customSession = session.fromPartition(`persist:${uuid.v4()}`); customSession.loadExtension(path.join(fixtures, 'extensions', 'devtools-extension')); - const winningMessage = emittedOnce(ipcMain, 'winning'); + const winningMessage = once(ipcMain, 'winning'); const w = new BrowserWindow({ show: true, webPreferences: { session: customSession, nodeIntegration: true, contextIsolation: false } }); await w.loadURL(url); w.webContents.openDevTools(); diff --git a/spec/fixtures/apps/remote-control/main.js b/spec/fixtures/apps/remote-control/main.js index b8d89ff72485..a9cbf2e9e170 100644 --- a/spec/fixtures/apps/remote-control/main.js +++ b/spec/fixtures/apps/remote-control/main.js @@ -1,6 +1,8 @@ const { app } = require('electron'); const http = require('http'); const v8 = require('v8'); +// eslint-disable-next-line camelcase +const promises_1 = require('timers/promises'); if (app.commandLine.hasSwitch('boot-eval')) { // eslint-disable-next-line no-eval diff --git a/spec/get-files.ts b/spec/get-files.ts index 3f3a15f81741..ad1c2452e6df 100644 --- a/spec/get-files.ts +++ b/spec/get-files.ts @@ -1,5 +1,5 @@ +import { once } from 'events'; import * as walkdir from 'walkdir'; -import { emittedOnce } from './lib/events-helpers'; export async function getFiles (directoryPath: string, { filter = null }: {filter?: ((file: string) => boolean) | null} = {}) { const files: string[] = []; @@ -9,6 +9,6 @@ export async function getFiles (directoryPath: string, { filter = null }: {filte walker.on('file', (file) => { if (!filter || filter(file)) { files.push(file); } }); - await emittedOnce(walker, 'end'); + await once(walker, 'end'); return files; } diff --git a/spec/guest-window-manager-spec.ts b/spec/guest-window-manager-spec.ts index 58d682254dbd..41968c934ff9 100644 --- a/spec/guest-window-manager-spec.ts +++ b/spec/guest-window-manager-spec.ts @@ -1,7 +1,7 @@ import { BrowserWindow } from 'electron'; import { expect, assert } from 'chai'; import { closeAllWindows } from './lib/window-helpers'; -const { emittedOnce } = require('./lib/events-helpers'); +import { once } from 'events'; describe('webContents.setWindowOpenHandler', () => { let browserWindow: BrowserWindow; @@ -173,13 +173,13 @@ describe('webContents.setWindowOpenHandler', () => { browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true"); }); - await emittedOnce(browserWindow.webContents, 'did-create-window'); + await once(browserWindow.webContents, 'did-create-window'); }); it('can change webPreferences of child windows', async () => { browserWindow.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { defaultFontSize: 30 } } })); - const didCreateWindow = emittedOnce(browserWindow.webContents, 'did-create-window'); + const didCreateWindow = once(browserWindow.webContents, 'did-create-window'); browserWindow.webContents.executeJavaScript("window.open('about:blank', '', 'show=no') && true"); const [childWindow] = await didCreateWindow; diff --git a/spec/lib/events-helpers.ts b/spec/lib/events-helpers.ts index 4cb3d231db77..b7cd12305ea2 100644 --- a/spec/lib/events-helpers.ts +++ b/spec/lib/events-helpers.ts @@ -3,53 +3,21 @@ * with events in async/await manner. */ -/** - * @param {!EventTarget} target - * @param {string} eventName - * @return {!Promise} - */ -export const waitForEvent = (target: EventTarget, eventName: string) => { - return new Promise(resolve => { - target.addEventListener(eventName, resolve, { once: true }); - }); -}; - -/** - * @param {!EventEmitter} emitter - * @param {string} eventName - * @return {!Promise} With Event as the first item. - */ -export const emittedOnce = (emitter: NodeJS.EventEmitter, eventName: string, trigger?: () => void) => { - return emittedNTimes(emitter, eventName, 1, trigger).then(([result]) => result); -}; +import { on } from 'events'; export const emittedNTimes = async (emitter: NodeJS.EventEmitter, eventName: string, times: number, trigger?: () => void) => { const events: any[][] = []; - const p = new Promise(resolve => { - const handler = (...args: any[]) => { - events.push(args); - if (events.length === times) { - emitter.removeListener(eventName, handler); - resolve(events); - } - }; - emitter.on(eventName, handler); - }); - if (trigger) { - await Promise.resolve(trigger()); + const iter = on(emitter, eventName); + if (trigger) await Promise.resolve(trigger()); + for await (const args of iter) { + events.push(args); + if (events.length === times) { break; } } - return p; + return events; }; export const emittedUntil = async (emitter: NodeJS.EventEmitter, eventName: string, untilFn: Function) => { - const p = new Promise(resolve => { - const handler = (...args: any[]) => { - if (untilFn(...args)) { - emitter.removeListener(eventName, handler); - resolve(args); - } - }; - emitter.on(eventName, handler); - }); - return p; + for await (const args of on(emitter, eventName)) { + if (untilFn(...args)) { return args; } + } }; diff --git a/spec/lib/spec-helpers.ts b/spec/lib/spec-helpers.ts index e132ad8872a6..6c4d1a774a94 100644 --- a/spec/lib/spec-helpers.ts +++ b/spec/lib/spec-helpers.ts @@ -21,8 +21,6 @@ const addOnly = (fn: Function): T => { export const ifit = (condition: boolean) => (condition ? it : addOnly(it.skip)); export const ifdescribe = (condition: boolean) => (condition ? describe : addOnly(describe.skip)); -export const delay = (time: number = 0) => new Promise(resolve => setTimeout(resolve, time)); - type CleanupFunction = (() => void) | (() => Promise) const cleanupFunctions: CleanupFunction[] = []; export async function runCleanupFunctions () { @@ -183,6 +181,7 @@ export async function itremote (name: string, fn: Function, args?: any[]) { const { ok, message } = await w.webContents.executeJavaScript(`(async () => { try { const chai_1 = require('chai') + const promises_1 = require('timers/promises') chai_1.use(require('chai-as-promised')) chai_1.use(require('dirty-chai')) await (${fn})(...${JSON.stringify(args ?? [])}) diff --git a/spec/lib/window-helpers.ts b/spec/lib/window-helpers.ts index ddc8d9f5b72c..82643fc2a0a6 100644 --- a/spec/lib/window-helpers.ts +++ b/spec/lib/window-helpers.ts @@ -1,6 +1,6 @@ import { expect } from 'chai'; import { BrowserWindow } from 'electron/main'; -import { emittedOnce } from './events-helpers'; +import { once } from 'events'; async function ensureWindowIsClosed (window: BrowserWindow | null) { if (window && !window.isDestroyed()) { @@ -10,7 +10,7 @@ async function ensureWindowIsClosed (window: BrowserWindow | null) { // children which need to be destroyed first. In that case, we // await the 'closed' event which signals the complete shutdown of the // window. - const isClosed = emittedOnce(window, 'closed'); + const isClosed = once(window, 'closed'); window.destroy(); await isClosed; } else { diff --git a/spec/logging-spec.ts b/spec/logging-spec.ts index f0b9b80bdc29..f7ee0d44d51b 100644 --- a/spec/logging-spec.ts +++ b/spec/logging-spec.ts @@ -1,11 +1,11 @@ import { app } from 'electron'; import { expect } from 'chai'; -import { emittedOnce } from './lib/events-helpers'; import { startRemoteControlApp, ifdescribe } from './lib/spec-helpers'; import * as fs from 'fs/promises'; import * as path from 'path'; import * as uuid from 'uuid'; +import { once } from 'events'; function isTestingBindingAvailable () { try { @@ -87,7 +87,7 @@ ifdescribe(isTestingBindingAvailable())('logging', () => { setTimeout(() => { app.quit(); }); return app.getPath('userData'); }); - await emittedOnce(rc.process, 'exit'); + await once(rc.process, 'exit'); const logFilePath = path.join(userDataDir, 'electron_debug.log'); const stat = await fs.stat(logFilePath); expect(stat.isFile()).to.be.true(); @@ -103,7 +103,7 @@ ifdescribe(isTestingBindingAvailable())('logging', () => { setTimeout(() => { app.quit(); }); return app.getPath('userData'); }); - await emittedOnce(rc.process, 'exit'); + await once(rc.process, 'exit'); const logFilePath = path.join(userDataDir, 'electron_debug.log'); const stat = await fs.stat(logFilePath); expect(stat.isFile()).to.be.true(); @@ -118,7 +118,7 @@ ifdescribe(isTestingBindingAvailable())('logging', () => { process._linkedBinding('electron_common_testing').log(0, 'TEST_LOG'); setTimeout(() => { require('electron').app.quit(); }); }); - await emittedOnce(rc.process, 'exit'); + await once(rc.process, 'exit'); const stat = await fs.stat(logFilePath); expect(stat.isFile()).to.be.true(); const contents = await fs.readFile(logFilePath, 'utf8'); @@ -132,7 +132,7 @@ ifdescribe(isTestingBindingAvailable())('logging', () => { process._linkedBinding('electron_common_testing').log(0, 'TEST_LOG'); setTimeout(() => { require('electron').app.quit(); }); }); - await emittedOnce(rc.process, 'exit'); + await once(rc.process, 'exit'); const stat = await fs.stat(logFilePath); expect(stat.isFile()).to.be.true(); const contents = await fs.readFile(logFilePath, 'utf8'); @@ -146,7 +146,7 @@ ifdescribe(isTestingBindingAvailable())('logging', () => { process._linkedBinding('electron_common_testing').log(0, 'LATER_LOG'); setTimeout(() => { require('electron').app.quit(); }); }); - await emittedOnce(rc.process, 'exit'); + await once(rc.process, 'exit'); const stat = await fs.stat(logFilePath); expect(stat.isFile()).to.be.true(); const contents = await fs.readFile(logFilePath, 'utf8'); diff --git a/spec/modules-spec.ts b/spec/modules-spec.ts index 6870b2f57cd0..a3e0468638a0 100644 --- a/spec/modules-spec.ts +++ b/spec/modules-spec.ts @@ -4,8 +4,8 @@ import * as fs from 'fs'; import { BrowserWindow } from 'electron/main'; import { ifdescribe, ifit } from './lib/spec-helpers'; import { closeAllWindows } from './lib/window-helpers'; -import { emittedOnce } from './lib/events-helpers'; import * as childProcess from 'child_process'; +import { once } from 'events'; const Module = require('module'); @@ -30,7 +30,7 @@ describe('modules support', () => { ifit(features.isRunAsNodeEnabled())('can be required in node binary', async function () { const child = childProcess.fork(path.join(fixtures, 'module', 'echo.js')); - const [msg] = await emittedOnce(child, 'message'); + const [msg] = await once(child, 'message'); expect(msg).to.equal('ok'); }); @@ -62,7 +62,7 @@ describe('modules support', () => { ifit(features.isRunAsNodeEnabled())('can be required in node binary', async function () { const child = childProcess.fork(path.join(fixtures, 'module', 'uv-dlopen.js')); - const [exitCode] = await emittedOnce(child, 'exit'); + const [exitCode] = await once(child, 'exit'); expect(exitCode).to.equal(0); }); }); diff --git a/spec/node-spec.ts b/spec/node-spec.ts index f5808c9c1086..3612bcde182a 100644 --- a/spec/node-spec.ts +++ b/spec/node-spec.ts @@ -3,10 +3,10 @@ import * as childProcess from 'child_process'; import * as fs from 'fs'; import * as path from 'path'; import * as util from 'util'; -import { emittedOnce } from './lib/events-helpers'; import { getRemoteContext, ifdescribe, ifit, itremote, useRemoteContext } from './lib/spec-helpers'; import { webContents } from 'electron/main'; import { EventEmitter } from 'stream'; +import { once } from 'events'; const features = process._linkedBinding('electron_common_features'); const mainFixturesPath = path.resolve(__dirname, 'fixtures'); @@ -18,7 +18,7 @@ describe('node feature', () => { describe('child_process.fork', () => { it('Works in browser process', async () => { const child = childProcess.fork(path.join(fixtures, 'module', 'ping.js')); - const message = emittedOnce(child, 'message'); + const message = once(child, 'message'); child.send('message'); const [msg] = await message; expect(msg).to.equal('message'); @@ -104,7 +104,7 @@ describe('node feature', () => { it('has the electron version in process.versions', async () => { const source = 'process.send(process.versions)'; const forked = require('child_process').fork('--eval', [source]); - const [message] = await emittedOnce(forked, 'message'); + const [message] = await once(forked, 'message'); expect(message) .to.have.own.property('electron') .that.is.a('string') @@ -158,7 +158,7 @@ describe('node feature', () => { cwd: path.join(mainFixturesPath, 'apps', 'libuv-hang'), stdio: 'inherit' }); - const [code] = await emittedOnce(appProcess, 'close'); + const [code] = await once(appProcess, 'close'); expect(code).to.equal(0); }); @@ -546,7 +546,7 @@ describe('node feature', () => { const env = { ...process.env, NODE_OPTIONS: '--v8-options' }; child = childProcess.spawn(process.execPath, { env }); - exitPromise = emittedOnce(child, 'exit'); + exitPromise = once(child, 'exit'); let output = ''; let success = false; @@ -609,7 +609,7 @@ describe('node feature', () => { }; // App should exit with code 1. const child = childProcess.spawn(process.execPath, [appPath], { env }); - const [code] = await emittedOnce(child, 'exit'); + const [code] = await once(child, 'exit'); expect(code).to.equal(1); }); @@ -622,7 +622,7 @@ describe('node feature', () => { }; // App should exit with code 0. const child = childProcess.spawn(process.execPath, [appPath], { env }); - const [code] = await emittedOnce(child, 'exit'); + const [code] = await once(child, 'exit'); expect(code).to.equal(0); }); }); @@ -642,7 +642,7 @@ describe('node feature', () => { child = childProcess.spawn(process.execPath, ['--force-fips'], { env: { ELECTRON_RUN_AS_NODE: 'true' } }); - exitPromise = emittedOnce(child, 'exit'); + exitPromise = once(child, 'exit'); let output = ''; const cleanup = () => { @@ -723,7 +723,7 @@ describe('node feature', () => { child = childProcess.spawn(process.execPath, ['--inspect=17364', path.join(fixtures, 'module', 'run-as-node.js')], { env: { ELECTRON_RUN_AS_NODE: 'true' } }); - exitPromise = emittedOnce(child, 'exit'); + exitPromise = once(child, 'exit'); let output = ''; const listener = (data: Buffer) => { output += data; }; @@ -734,7 +734,7 @@ describe('node feature', () => { child.stderr.on('data', listener); child.stdout.on('data', listener); - await emittedOnce(child, 'exit'); + await once(child, 'exit'); cleanup(); if (/^Debugger listening on ws:/m.test(output)) { expect(output.trim()).to.contain(':17364', 'should be listening on port 17364'); @@ -745,13 +745,13 @@ describe('node feature', () => { 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'); + exitPromise = once(child, 'exit'); let output = ''; const listener = (data: Buffer) => { output += data; }; child.stderr.on('data', listener); child.stdout.on('data', listener); - await emittedOnce(child, 'exit'); + await once(child, 'exit'); if (output.trim().startsWith('Debugger listening on ws://')) { throw new Error('Inspector was started when it should not have been'); } @@ -762,7 +762,7 @@ describe('node feature', () => { child = childProcess.spawn(process.execPath, [path.join(fixtures, 'module', 'delay-exit'), '--inspect=0'], { stdio: ['ipc'] }) as childProcess.ChildProcessWithoutNullStreams; - exitPromise = emittedOnce(child, 'exit'); + exitPromise = once(child, 'exit'); const cleanup = () => { child.stderr.removeListener('data', listener); @@ -809,9 +809,9 @@ describe('node feature', () => { env: { ELECTRON_RUN_AS_NODE: 'true' }, stdio: ['ipc'] }) as childProcess.ChildProcessWithoutNullStreams; - exitPromise = emittedOnce(child, 'exit'); + exitPromise = once(child, 'exit'); - const [{ cmd, debuggerEnabled, success }] = await emittedOnce(child, 'message'); + const [{ cmd, debuggerEnabled, success }] = await once(child, 'message'); expect(cmd).to.equal('assert'); expect(debuggerEnabled).to.be.true(); expect(success).to.be.true(); @@ -828,7 +828,7 @@ describe('node feature', () => { const child = childProcess.spawn(process.execPath, [scriptPath], { env: { ELECTRON_RUN_AS_NODE: 'true' } }); - const [code, signal] = await emittedOnce(child, 'exit'); + const [code, signal] = await once(child, 'exit'); expect(code).to.equal(0); expect(signal).to.equal(null); child.kill(); diff --git a/spec/security-warnings-spec.ts b/spec/security-warnings-spec.ts index 1e3d351e61ce..bc63a6a95412 100644 --- a/spec/security-warnings-spec.ts +++ b/spec/security-warnings-spec.ts @@ -8,7 +8,8 @@ import { BrowserWindow, WebPreferences } from 'electron/main'; import { closeWindow } from './lib/window-helpers'; import { emittedUntil } from './lib/events-helpers'; -import { delay, listen } from './lib/spec-helpers'; +import { listen } from './lib/spec-helpers'; +import { setTimeout } from 'timers/promises'; const messageContainsSecurityWarning = (event: Event, level: number, message: string) => { return message.indexOf('Electron Security Warning') > -1; @@ -140,7 +141,7 @@ describe('security warnings', () => { w.webContents.on('console-message', () => { didNotWarn = false; }); - await delay(500); + await setTimeout(500); expect(didNotWarn).to.equal(true); }); diff --git a/spec/spellchecker-spec.ts b/spec/spellchecker-spec.ts index 4a63c437f88c..73a82ae85d82 100644 --- a/spec/spellchecker-spec.ts +++ b/spec/spellchecker-spec.ts @@ -5,8 +5,9 @@ import * as path from 'path'; import * as fs from 'fs'; import * as http from 'http'; import { closeWindow } from './lib/window-helpers'; -import { emittedOnce } from './lib/events-helpers'; -import { ifit, ifdescribe, delay, listen } from './lib/spec-helpers'; +import { ifit, ifdescribe, listen } from './lib/spec-helpers'; +import { once } from 'events'; +import { setTimeout } from 'timers/promises'; const features = process._linkedBinding('electron_common_features'); const v8Util = process._linkedBinding('electron_common_v8_util'); @@ -17,7 +18,7 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', function () let w: BrowserWindow; async function rightClick () { - const contextMenuPromise = emittedOnce(w.webContents, 'context-menu'); + const contextMenuPromise = once(w.webContents, 'context-menu'); w.webContents.sendInputEvent({ type: 'mouseDown', button: 'right', @@ -35,7 +36,7 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', function () const timeout = (process.env.IS_ASAN ? 180 : 10) * 1000; let contextMenuParams = await rightClick(); while (!fn(contextMenuParams) && (Date.now() - now < timeout)) { - await delay(100); + await setTimeout(100); contextMenuParams = await rightClick(); } return contextMenuParams; @@ -107,7 +108,7 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', function () ifit(shouldRun)('should detect incorrectly spelled words as incorrect after disabling all languages and re-enabling', async () => { w.webContents.session.setSpellCheckerLanguages([]); - await delay(500); + await setTimeout(500); w.webContents.session.setSpellCheckerLanguages(['en-US']); await w.webContents.executeJavaScript('document.body.querySelector("textarea").value = "typograpy"'); await w.webContents.executeJavaScript('document.body.querySelector("textarea").focus()'); @@ -147,13 +148,13 @@ ifdescribe(features.isBuiltinSpellCheckerEnabled())('spellchecker', function () // spellCheckerEnabled is sent to renderer asynchronously and there is // no event notifying when it is finished, so wait a little while to // ensure the setting has been changed in renderer. - await delay(500); + await setTimeout(500); expect(await callWebFrameFn('isWordMisspelled("typograpy")')).to.equal(false); w.webContents.session.spellCheckerEnabled = true; v8Util.runUntilIdle(); expect(w.webContents.session.spellCheckerEnabled).to.be.true(); - await delay(500); + await setTimeout(500); expect(await callWebFrameFn('isWordMisspelled("typograpy")')).to.equal(true); }); }); diff --git a/spec/visibility-state-spec.ts b/spec/visibility-state-spec.ts index 89df7225e1a5..0d332536db14 100644 --- a/spec/visibility-state-spec.ts +++ b/spec/visibility-state-spec.ts @@ -3,9 +3,10 @@ import * as cp from 'child_process'; import { BrowserWindow, BrowserWindowConstructorOptions, ipcMain } from 'electron/main'; import * as path from 'path'; -import { emittedOnce } from './lib/events-helpers'; import { closeWindow } from './lib/window-helpers'; -import { ifdescribe, delay } from './lib/spec-helpers'; +import { ifdescribe } from './lib/spec-helpers'; +import { once } from 'events'; +import { setTimeout } from 'timers/promises'; // visibilityState specs pass on linux with a real window manager but on CI // the environment does not let these specs pass @@ -35,7 +36,7 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { itWithOptions('should be visible when the window is initially shown by default', {}, async () => { load(); - const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, state] = await once(ipcMain, 'initial-visibility-state'); expect(state).to.equal('visible'); }); @@ -43,7 +44,7 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { show: true }, async () => { load(); - const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, state] = await once(ipcMain, 'initial-visibility-state'); expect(state).to.equal('visible'); }); @@ -51,7 +52,7 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { show: false }, async () => { load(); - const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, state] = await once(ipcMain, 'initial-visibility-state'); expect(state).to.equal('hidden'); }); @@ -60,7 +61,7 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { }, async () => { w.show(); load(); - const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, state] = await once(ipcMain, 'initial-visibility-state'); expect(state).to.equal('visible'); }); @@ -70,40 +71,42 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { // TODO(MarshallOfSound): Figure out if we can work around this 1 tick issue for users if (process.platform === 'darwin') { // Wait for a tick, the window being "shown" takes 1 tick on macOS - await delay(10000); + await setTimeout(10000); } w.hide(); load(); - const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, state] = await once(ipcMain, 'initial-visibility-state'); expect(state).to.equal('hidden'); }); itWithOptions('should be toggle between visible and hidden as the window is hidden and shown', {}, async () => { load(); - const [, initialState] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, initialState] = await once(ipcMain, 'initial-visibility-state'); expect(initialState).to.equal('visible'); w.hide(); - await emittedOnce(ipcMain, 'visibility-change-hidden'); + await once(ipcMain, 'visibility-change-hidden'); w.show(); - await emittedOnce(ipcMain, 'visibility-change-visible'); + await once(ipcMain, 'visibility-change-visible'); }); itWithOptions('should become hidden when a window is minimized', {}, async () => { load(); - const [, initialState] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, initialState] = await once(ipcMain, 'initial-visibility-state'); expect(initialState).to.equal('visible'); w.minimize(); - await emittedOnce(ipcMain, 'visibility-change-hidden', () => w.minimize()); + const p = once(ipcMain, 'visibility-change-hidden'); + w.minimize(); + await p; }); itWithOptions('should become visible when a window is restored', {}, async () => { load(); - const [, initialState] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, initialState] = await once(ipcMain, 'initial-visibility-state'); expect(initialState).to.equal('visible'); w.minimize(); - await emittedOnce(ipcMain, 'visibility-change-hidden'); + await once(ipcMain, 'visibility-change-hidden'); w.restore(); - await emittedOnce(ipcMain, 'visibility-change-visible'); + await once(ipcMain, 'visibility-change-visible'); }); describe('on platforms that support occlusion detection', () => { @@ -141,7 +144,7 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { height: 200 }); load(); - const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, state] = await once(ipcMain, 'initial-visibility-state'); expect(state).to.equal('visible'); }); @@ -158,7 +161,7 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { height: 200 }); load(); - const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, state] = await once(ipcMain, 'initial-visibility-state'); expect(state).to.equal('visible'); }); @@ -170,7 +173,7 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { }, async function () { this.timeout(240000); load(); - const [, state] = await emittedOnce(ipcMain, 'initial-visibility-state'); + const [, state] = await once(ipcMain, 'initial-visibility-state'); expect(state).to.equal('visible'); makeOtherWindow({ x: 0, @@ -178,7 +181,7 @@ ifdescribe(process.platform !== 'linux')('document.visibilityState', () => { width: 300, height: 300 }); - await emittedOnce(ipcMain, 'visibility-change-hidden'); + await once(ipcMain, 'visibility-change-hidden'); }); }); }); diff --git a/spec/webview-spec.ts b/spec/webview-spec.ts index 58ddb04e9ad1..c6a4d514d91e 100644 --- a/spec/webview-spec.ts +++ b/spec/webview-spec.ts @@ -2,11 +2,13 @@ import * as path from 'path'; import * as url from 'url'; import { BrowserWindow, session, ipcMain, app, WebContents } from 'electron/main'; import { closeAllWindows } from './lib/window-helpers'; -import { emittedOnce, emittedUntil } from './lib/events-helpers'; -import { ifit, ifdescribe, delay, defer, itremote, useRemoteContext, listen } from './lib/spec-helpers'; +import { emittedUntil } from './lib/events-helpers'; +import { ifit, ifdescribe, defer, itremote, useRemoteContext, listen } from './lib/spec-helpers'; import { expect } from 'chai'; import * as http from 'http'; import * as auth from 'basic-auth'; +import { once } from 'events'; +import { setTimeout } from 'timers/promises'; declare let WebView: any; const features = process._linkedBinding('electron_common_features'); @@ -85,7 +87,7 @@ describe(' tag', function () { } }); w.loadFile(path.join(fixtures, 'pages', 'webview-no-script.html')); - await emittedOnce(ipcMain, 'pong'); + await once(ipcMain, 'pong'); }); it('works with sandbox', async () => { @@ -97,7 +99,7 @@ describe(' tag', function () { } }); w.loadFile(path.join(fixtures, 'pages', 'webview-isolated.html')); - await emittedOnce(ipcMain, 'pong'); + await once(ipcMain, 'pong'); }); it('works with contextIsolation', async () => { @@ -109,7 +111,7 @@ describe(' tag', function () { } }); w.loadFile(path.join(fixtures, 'pages', 'webview-isolated.html')); - await emittedOnce(ipcMain, 'pong'); + await once(ipcMain, 'pong'); }); it('works with contextIsolation + sandbox', async () => { @@ -122,7 +124,7 @@ describe(' tag', function () { } }); w.loadFile(path.join(fixtures, 'pages', 'webview-isolated.html')); - await emittedOnce(ipcMain, 'pong'); + await once(ipcMain, 'pong'); }); it('works with Trusted Types', async () => { @@ -133,7 +135,7 @@ describe(' tag', function () { } }); w.loadFile(path.join(fixtures, 'pages', 'webview-trusted-types.html')); - await emittedOnce(ipcMain, 'pong'); + await once(ipcMain, 'pong'); }); it('is disabled by default', async () => { @@ -145,7 +147,7 @@ describe(' tag', function () { } }); - const webview = emittedOnce(ipcMain, 'webview'); + const webview = once(ipcMain, 'webview'); w.loadFile(path.join(fixtures, 'pages', 'webview-no-script.html')); const [, type] = await webview; @@ -163,11 +165,11 @@ describe(' tag', function () { it('updates when the window is shown after the ready-to-show event', async () => { const w = new BrowserWindow({ show: false }); - const readyToShowSignal = emittedOnce(w, 'ready-to-show'); - const pongSignal1 = emittedOnce(ipcMain, 'pong'); + const readyToShowSignal = once(w, 'ready-to-show'); + const pongSignal1 = once(ipcMain, 'pong'); w.loadFile(path.join(fixtures, 'pages', 'webview-visibilitychange.html')); await pongSignal1; - const pongSignal2 = emittedOnce(ipcMain, 'pong'); + const pongSignal2 = once(ipcMain, 'pong'); await readyToShowSignal; w.show(); @@ -179,13 +181,13 @@ describe(' tag', function () { it('inherits the parent window visibility state and receives visibilitychange events', async () => { const w = new BrowserWindow({ show: false }); w.loadFile(path.join(fixtures, 'pages', 'webview-visibilitychange.html')); - const [, visibilityState, hidden] = await emittedOnce(ipcMain, 'pong'); + const [, visibilityState, hidden] = await once(ipcMain, 'pong'); expect(visibilityState).to.equal('hidden'); expect(hidden).to.be.true(); // We have to start waiting for the event // before we ask the webContents to resize. - const getResponse = emittedOnce(ipcMain, 'pong'); + const getResponse = once(ipcMain, 'pong'); w.webContents.emit('-window-visibility-change', 'visible'); return getResponse.then(([, visibilityState, hidden]) => { @@ -206,8 +208,8 @@ describe(' tag', function () { contextIsolation: false } }); - const didAttachWebview = emittedOnce(w.webContents, 'did-attach-webview'); - const webviewDomReady = emittedOnce(ipcMain, 'webview-dom-ready'); + const didAttachWebview = once(w.webContents, 'did-attach-webview'); + const webviewDomReady = once(ipcMain, 'webview-dom-ready'); w.loadFile(path.join(fixtures, 'pages', 'webview-did-attach-event.html')); const [, webContents] = await didAttachWebview; @@ -305,7 +307,7 @@ describe(' tag', function () { }); }); - const [, { runtimeId, tabId }] = await emittedOnce(ipcMain, 'answer'); + const [, { runtimeId, tabId }] = await once(ipcMain, 'answer'); expect(runtimeId).to.match(/^[a-z]{32}$/); expect(tabId).to.equal(childWebContentsId); await w.webContents.executeJavaScript('webview.closeDevTools()'); @@ -340,7 +342,7 @@ describe(' tag', function () { contextIsolation: false } }); - const zoomEventPromise = emittedOnce(ipcMain, 'webview-parent-zoom-level'); + const zoomEventPromise = once(ipcMain, 'webview-parent-zoom-level'); w.loadFile(path.join(fixtures, 'pages', 'webview-zoom-factor.html')); const [, zoomFactor, zoomLevel] = await zoomEventPromise; @@ -417,7 +419,7 @@ describe(' tag', function () { }); w.loadFile(path.join(fixtures, 'pages', 'webview-origin-zoom-level.html')); - const [, zoomLevel] = await emittedOnce(ipcMain, 'webview-origin-zoom-level'); + const [, zoomLevel] = await once(ipcMain, 'webview-origin-zoom-level'); expect(zoomLevel).to.equal(2.0); }); @@ -432,8 +434,8 @@ describe(' tag', function () { contextIsolation: false } }); - const attachPromise = emittedOnce(w.webContents, 'did-attach-webview'); - const readyPromise = emittedOnce(ipcMain, 'dom-ready'); + const attachPromise = once(w.webContents, 'did-attach-webview'); + const readyPromise = once(ipcMain, 'dom-ready'); w.loadFile(path.join(fixtures, 'pages', 'webview-zoom-inherited.html')); const [, webview] = await attachPromise; await readyPromise; @@ -451,7 +453,7 @@ describe(' tag', function () { contextIsolation: false } }); - const attachPromise = emittedOnce(w.webContents, 'did-attach-webview'); + const attachPromise = once(w.webContents, 'did-attach-webview'); await w.loadFile(path.join(fixtures, 'pages', 'webview-zoom-inherited.html')); await attachPromise; await w.webContents.executeJavaScript('view.remove()'); @@ -470,9 +472,9 @@ describe(' tag', function () { } }); - const attachPromise = emittedOnce(w.webContents, 'did-attach-webview'); - const loadPromise = emittedOnce(w.webContents, 'did-finish-load'); - const readyPromise = emittedOnce(ipcMain, 'webview-ready'); + const attachPromise = once(w.webContents, 'did-attach-webview'); + const loadPromise = once(w.webContents, 'did-finish-load'); + const readyPromise = once(ipcMain, 'webview-ready'); w.loadFile(path.join(__dirname, 'fixtures', 'webview', 'fullscreen', 'main.html')); @@ -485,7 +487,7 @@ describe(' tag', function () { afterEach(async () => { // The leaving animation is un-observable but can interfere with future tests // Specifically this is async on macOS but can be on other platforms too - await delay(1000); + await setTimeout(1000); closeAllWindows(); }); @@ -494,13 +496,13 @@ describe(' tag', function () { const [w, webview] = await loadWebViewWindow(); expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.false(); - const parentFullscreen = emittedOnce(ipcMain, 'fullscreenchange'); + const parentFullscreen = once(ipcMain, 'fullscreenchange'); await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true); await parentFullscreen; expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.true(); - const close = emittedOnce(w, 'closed'); + const close = once(w, 'closed'); w.close(); await close; }); @@ -509,9 +511,9 @@ describe(' tag', function () { const [w, webview] = await loadWebViewWindow(); expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.false(); - const parentFullscreen = emittedOnce(ipcMain, 'fullscreenchange'); - const enterHTMLFS = emittedOnce(w.webContents, 'enter-html-full-screen'); - const leaveHTMLFS = emittedOnce(w.webContents, 'leave-html-full-screen'); + const parentFullscreen = once(ipcMain, 'fullscreenchange'); + const enterHTMLFS = once(w.webContents, 'enter-html-full-screen'); + const leaveHTMLFS = once(w.webContents, 'leave-html-full-screen'); await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true); expect(await w.webContents.executeJavaScript('isIframeFullscreen()')).to.be.true(); @@ -519,7 +521,7 @@ describe(' tag', function () { await webview.executeJavaScript('document.exitFullscreen()'); await Promise.all([enterHTMLFS, leaveHTMLFS, parentFullscreen]); - const close = emittedOnce(w, 'closed'); + const close = once(w, 'closed'); w.close(); await close; }); @@ -527,17 +529,17 @@ describe(' tag', function () { // FIXME(zcbenz): Fullscreen events do not work on Linux. ifit(process.platform !== 'linux')('exiting fullscreen should unfullscreen window', async () => { const [w, webview] = await loadWebViewWindow(); - const enterFullScreen = emittedOnce(w, 'enter-full-screen'); + const enterFullScreen = once(w, 'enter-full-screen'); await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true); await enterFullScreen; - const leaveFullScreen = emittedOnce(w, 'leave-full-screen'); + const leaveFullScreen = once(w, 'leave-full-screen'); await webview.executeJavaScript('document.exitFullscreen()', true); await leaveFullScreen; - await delay(0); + await setTimeout(); expect(w.isFullScreen()).to.be.false(); - const close = emittedOnce(w, 'closed'); + const close = once(w, 'closed'); w.close(); await close; }); @@ -545,17 +547,17 @@ describe(' tag', function () { // Sending ESC via sendInputEvent only works on Windows. ifit(process.platform === 'win32')('pressing ESC should unfullscreen window', async () => { const [w, webview] = await loadWebViewWindow(); - const enterFullScreen = emittedOnce(w, 'enter-full-screen'); + const enterFullScreen = once(w, 'enter-full-screen'); await webview.executeJavaScript('document.getElementById("div").requestFullscreen()', true); await enterFullScreen; - const leaveFullScreen = emittedOnce(w, 'leave-full-screen'); + const leaveFullScreen = once(w, 'leave-full-screen'); w.webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Escape' }); await leaveFullScreen; - await delay(0); + await setTimeout(); expect(w.isFullScreen()).to.be.false(); - const close = emittedOnce(w, 'closed'); + const close = once(w, 'closed'); w.close(); await close; }); @@ -570,24 +572,24 @@ describe(' tag', function () { } }); - const didAttachWebview = emittedOnce(w.webContents, 'did-attach-webview'); + const didAttachWebview = once(w.webContents, 'did-attach-webview'); w.loadFile(path.join(fixtures, 'pages', 'webview-did-attach-event.html')); const [, webContents] = await didAttachWebview; - const enterFSWindow = emittedOnce(w, 'enter-html-full-screen'); - const enterFSWebview = emittedOnce(webContents, 'enter-html-full-screen'); + const enterFSWindow = once(w, 'enter-html-full-screen'); + const enterFSWebview = once(webContents, 'enter-html-full-screen'); await webContents.executeJavaScript('document.getElementById("div").requestFullscreen()', true); await enterFSWindow; await enterFSWebview; - const leaveFSWindow = emittedOnce(w, 'leave-html-full-screen'); - const leaveFSWebview = emittedOnce(webContents, 'leave-html-full-screen'); + const leaveFSWindow = once(w, 'leave-html-full-screen'); + const leaveFSWebview = once(webContents, 'leave-html-full-screen'); webContents.sendInputEvent({ type: 'keyDown', keyCode: 'Escape' }); await leaveFSWebview; await leaveFSWindow; - const close = emittedOnce(w, 'closed'); + const close = once(w, 'closed'); w.close(); await close; }); @@ -595,14 +597,14 @@ describe(' tag', function () { it('should support user gesture', async () => { const [w, webview] = await loadWebViewWindow(); - const waitForEnterHtmlFullScreen = emittedOnce(webview, 'enter-html-full-screen'); + const waitForEnterHtmlFullScreen = once(webview, 'enter-html-full-screen'); const jsScript = "document.querySelector('video').webkitRequestFullscreen()"; webview.executeJavaScript(jsScript, true); await waitForEnterHtmlFullScreen; - const close = emittedOnce(w, 'closed'); + const close = once(w, 'closed'); w.close(); await close; }); @@ -625,7 +627,7 @@ describe(' tag', function () { src: `file://${path.join(fixtures, 'api', 'native-window-open-blank.html')}` }); - const [, content] = await emittedOnce(ipcMain, 'answer'); + const [, content] = await once(ipcMain, 'answer'); expect(content).to.equal('Hello'); }); @@ -638,7 +640,7 @@ describe(' tag', function () { src: `file://${path.join(fixtures, 'api', 'native-window-open-file.html')}` }); - const [, content] = await emittedOnce(ipcMain, 'answer'); + const [, content] = await once(ipcMain, 'answer'); expect(content).to.equal('Hello'); }); @@ -650,7 +652,7 @@ describe(' tag', function () { src: `file://${path.join(fixtures, 'api', 'native-window-open-no-allowpopups.html')}` }); - const [, { windowOpenReturnedNull }] = await emittedOnce(ipcMain, 'answer'); + const [, { windowOpenReturnedNull }] = await once(ipcMain, 'answer'); expect(windowOpenReturnedNull).to.be.true(); }); @@ -663,7 +665,7 @@ describe(' tag', function () { src: `file://${path.join(fixtures, 'api', 'native-window-open-cross-origin.html')}` }); - const [, content] = await emittedOnce(ipcMain, 'answer'); + const [, content] = await once(ipcMain, 'answer'); const expectedContent = 'Blocked a frame with origin "file://" from accessing a cross-origin frame.'; @@ -678,7 +680,7 @@ describe(' tag', function () { src: `file://${fixtures}/pages/window-open.html` }); - await emittedOnce(app, 'browser-window-created'); + await once(app, 'browser-window-created'); }); it('emits a web-contents-created event', async () => { @@ -699,7 +701,7 @@ describe(' tag', function () { allowpopups: 'on', src: `file://${path.join(fixtures, 'api', 'native-window-open-noopener.html')}` }); - await emittedOnce(app, 'browser-window-created'); + await once(app, 'browser-window-created'); }); }); @@ -719,7 +721,7 @@ describe(' tag', function () { webpreferences: 'contextIsolation=yes' }); - const [, data] = await emittedOnce(ipcMain, 'isolated-world'); + const [, data] = await once(ipcMain, 'isolated-world'); expect(data).to.deep.equal({ preloadContext: { preloadProperty: 'number', @@ -784,55 +786,55 @@ describe(' tag', function () { // "PermissionDeniedError". It should be re-enabled if we find a way to mock // the presence of a microphone & camera. xit('emits when using navigator.getUserMedia api', async () => { - const errorFromRenderer = emittedOnce(ipcMain, 'message'); + const errorFromRenderer = once(ipcMain, 'message'); loadWebView(w.webContents, { src: `file://${fixtures}/pages/permissions/media.html`, partition, nodeintegration: 'on' }); - const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + const [, webViewContents] = await once(app, 'web-contents-created'); setUpRequestHandler(webViewContents.id, 'media'); const [, errorName] = await errorFromRenderer; expect(errorName).to.equal('PermissionDeniedError'); }); it('emits when using navigator.geolocation api', async () => { - const errorFromRenderer = emittedOnce(ipcMain, 'message'); + const errorFromRenderer = once(ipcMain, 'message'); loadWebView(w.webContents, { src: `file://${fixtures}/pages/permissions/geolocation.html`, partition, nodeintegration: 'on', webpreferences: 'contextIsolation=no' }); - const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + const [, webViewContents] = await once(app, 'web-contents-created'); setUpRequestHandler(webViewContents.id, 'geolocation'); const [, error] = await errorFromRenderer; expect(error).to.equal('User denied Geolocation'); }); it('emits when using navigator.requestMIDIAccess without sysex api', async () => { - const errorFromRenderer = emittedOnce(ipcMain, 'message'); + const errorFromRenderer = once(ipcMain, 'message'); loadWebView(w.webContents, { src: `file://${fixtures}/pages/permissions/midi.html`, partition, nodeintegration: 'on', webpreferences: 'contextIsolation=no' }); - const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + const [, webViewContents] = await once(app, 'web-contents-created'); setUpRequestHandler(webViewContents.id, 'midi'); const [, error] = await errorFromRenderer; expect(error).to.equal('SecurityError'); }); it('emits when using navigator.requestMIDIAccess with sysex api', async () => { - const errorFromRenderer = emittedOnce(ipcMain, 'message'); + const errorFromRenderer = once(ipcMain, 'message'); loadWebView(w.webContents, { src: `file://${fixtures}/pages/permissions/midi-sysex.html`, partition, nodeintegration: 'on', webpreferences: 'contextIsolation=no' }); - const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + const [, webViewContents] = await once(app, 'web-contents-created'); setUpRequestHandler(webViewContents.id, 'midiSysex'); const [, error] = await errorFromRenderer; expect(error).to.equal('SecurityError'); @@ -843,19 +845,19 @@ describe(' tag', function () { src: 'magnet:test', partition }); - const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + const [, webViewContents] = await once(app, 'web-contents-created'); await setUpRequestHandler(webViewContents.id, 'openExternal'); }); it('emits when using Notification.requestPermission', async () => { - const errorFromRenderer = emittedOnce(ipcMain, 'message'); + const errorFromRenderer = once(ipcMain, 'message'); loadWebView(w.webContents, { src: `file://${fixtures}/pages/permissions/notification.html`, partition, nodeintegration: 'on', webpreferences: 'contextIsolation=no' }); - const [, webViewContents] = await emittedOnce(app, 'web-contents-created'); + const [, webViewContents] = await once(app, 'web-contents-created'); await setUpRequestHandler(webViewContents.id, 'notifications'); @@ -1141,12 +1143,12 @@ describe(' tag', function () { w.setAttribute('preload', `file://${fixtures}/module/preload-ipc.js`); w.setAttribute('src', `file://${fixtures}/pages/ipc-message.html`); document.body.appendChild(w); - const { frameId } = await new Promise(resolve => w.addEventListener('ipc-message', resolve, { once: true })); + const { frameId } = await new Promise(resolve => w.addEventListener('ipc-message', resolve, { once: true })); const message = 'boom!'; w.sendToFrame(frameId, 'ping', message); - const { channel, args } = await new Promise(resolve => w.addEventListener('ipc-message', resolve, { once: true })); + const { channel, args } = await new Promise(resolve => w.addEventListener('ipc-message', resolve, { once: true })); expect(channel).to.equal('pong'); expect(args).to.deep.equal([message]); @@ -1642,7 +1644,7 @@ describe(' tag', function () { itremote('does not emit when src is not changed', async () => { const webview = new WebView(); document.body.appendChild(webview); - await new Promise(resolve => setTimeout(resolve)); + await setTimeout(); 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); });