|
@@ -7,7 +7,7 @@ import * as http from 'http';
|
|
|
import { AddressInfo } from 'net';
|
|
|
import { app, BrowserWindow, BrowserView, ipcMain, OnBeforeSendHeadersListenerDetails, protocol, screen, webContents, session, WebContents } from 'electron/main';
|
|
|
|
|
|
-import { emittedOnce } from './events-helpers';
|
|
|
+import { emittedOnce, emittedUntil } from './events-helpers';
|
|
|
import { ifit, ifdescribe } from './spec-helpers';
|
|
|
import { closeWindow, closeAllWindows } from './window-helpers';
|
|
|
|
|
@@ -38,6 +38,10 @@ const expectBoundsEqual = (actual: any, expected: any) => {
|
|
|
}
|
|
|
};
|
|
|
|
|
|
+const isBeforeUnload = (event: Event, level: number, message: string) => {
|
|
|
+ return (message === 'beforeunload');
|
|
|
+};
|
|
|
+
|
|
|
describe('BrowserWindow module', () => {
|
|
|
describe('BrowserWindow constructor', () => {
|
|
|
it('allows passing void 0 as the webContents', async () => {
|
|
@@ -95,16 +99,11 @@ describe('BrowserWindow module', () => {
|
|
|
fs.unlinkSync(test);
|
|
|
expect(String(content)).to.equal('unload');
|
|
|
});
|
|
|
+
|
|
|
it('should emit beforeunload handler', async () => {
|
|
|
await w.loadFile(path.join(fixtures, 'api', 'beforeunload-false.html'));
|
|
|
- const beforeunload = new Promise(resolve => {
|
|
|
- ipcMain.once('onbeforeunload', (e) => {
|
|
|
- e.returnValue = null;
|
|
|
- resolve();
|
|
|
- });
|
|
|
- });
|
|
|
w.close();
|
|
|
- await beforeunload;
|
|
|
+ await emittedOnce(w.webContents, 'before-unload-fired');
|
|
|
});
|
|
|
|
|
|
describe('when invoked synchronously inside navigation observer', () => {
|
|
@@ -185,13 +184,11 @@ describe('BrowserWindow module', () => {
|
|
|
fs.unlinkSync(test);
|
|
|
expect(content).to.equal('close');
|
|
|
});
|
|
|
+
|
|
|
it('should emit beforeunload event', async function () {
|
|
|
- // TODO(nornagon): deflake this test.
|
|
|
- this.retries(3);
|
|
|
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-false.html'));
|
|
|
- w.webContents.executeJavaScript('run()', true);
|
|
|
- const [e] = await emittedOnce(ipcMain, 'onbeforeunload');
|
|
|
- e.returnValue = null;
|
|
|
+ w.webContents.executeJavaScript('window.close()', true);
|
|
|
+ await emittedOnce(w.webContents, 'before-unload-fired');
|
|
|
});
|
|
|
});
|
|
|
|
|
@@ -2629,32 +2626,31 @@ describe('BrowserWindow module', () => {
|
|
|
});
|
|
|
|
|
|
describe('beforeunload handler', function () {
|
|
|
- // TODO(nornagon): I feel like these tests _oughtn't_ be flakey, but
|
|
|
- // beforeunload is in general not reliable on the web, so i'm not going to
|
|
|
- // worry about it too much for now.
|
|
|
- this.retries(3);
|
|
|
-
|
|
|
let w: BrowserWindow = null as unknown as BrowserWindow;
|
|
|
beforeEach(() => {
|
|
|
w = new BrowserWindow({ show: false, webPreferences: { nodeIntegration: true } });
|
|
|
});
|
|
|
- afterEach(() => {
|
|
|
- ipcMain.removeAllListeners('onbeforeunload');
|
|
|
- });
|
|
|
afterEach(closeAllWindows);
|
|
|
- it('returning undefined would not prevent close', (done) => {
|
|
|
- w.once('closed', () => { done(); });
|
|
|
- w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-undefined.html'));
|
|
|
+
|
|
|
+ it('returning undefined would not prevent close', async () => {
|
|
|
+ await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-undefined.html'));
|
|
|
+ const wait = emittedOnce(w, 'closed');
|
|
|
+ w.close();
|
|
|
+ await wait;
|
|
|
});
|
|
|
+
|
|
|
it('returning false would prevent close', async () => {
|
|
|
await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-false.html'));
|
|
|
- w.webContents.executeJavaScript('run()', true);
|
|
|
- const [e] = await emittedOnce(ipcMain, 'onbeforeunload');
|
|
|
- e.returnValue = null;
|
|
|
+ w.close();
|
|
|
+ const [, proceed] = await emittedOnce(w.webContents, 'before-unload-fired');
|
|
|
+ expect(proceed).to.equal(false);
|
|
|
});
|
|
|
- it('returning empty string would prevent close', (done) => {
|
|
|
- ipcMain.once('onbeforeunload', (e) => { e.returnValue = null; done(); });
|
|
|
- w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-empty-string.html'));
|
|
|
+
|
|
|
+ it('returning empty string would prevent close', async () => {
|
|
|
+ await w.loadFile(path.join(__dirname, 'fixtures', 'api', 'close-beforeunload-empty-string.html'));
|
|
|
+ w.close();
|
|
|
+ const [, proceed] = await emittedOnce(w.webContents, 'before-unload-fired');
|
|
|
+ expect(proceed).to.equal(false);
|
|
|
});
|
|
|
|
|
|
it('emits for each close attempt', async () => {
|
|
@@ -2663,46 +2659,16 @@ describe('BrowserWindow module', () => {
|
|
|
const destroyListener = () => { expect.fail('Close was not prevented'); };
|
|
|
w.webContents.once('destroyed', destroyListener);
|
|
|
|
|
|
- await w.webContents.executeJavaScript('preventNextBeforeUnload()', true);
|
|
|
- {
|
|
|
- const p = emittedOnce(ipcMain, 'onbeforeunload');
|
|
|
- w.close();
|
|
|
- const [e] = await p;
|
|
|
- e.returnValue = null;
|
|
|
- }
|
|
|
-
|
|
|
- await w.webContents.executeJavaScript('preventNextBeforeUnload()', true);
|
|
|
-
|
|
|
- // Hi future test refactorer! I don't know what event this timeout allows
|
|
|
- // to occur, but without it, this test becomes flaky at this point and
|
|
|
- // sometimes the window gets closed even though a `beforeunload` handler
|
|
|
- // has been installed. I looked for events being emitted by the
|
|
|
- // `webContents` during this timeout period and found nothing, so it
|
|
|
- // might be some sort of internal timeout being applied by the content/
|
|
|
- // layer, or blink?
|
|
|
- //
|
|
|
- // In any case, this incantation reduces flakiness. I'm going to add a
|
|
|
- // summoning circle for good measure.
|
|
|
- //
|
|
|
- // 🕯 🕯
|
|
|
- // 🕯 🕯
|
|
|
- // 🕯 🕯
|
|
|
- await new Promise(resolve => setTimeout(resolve, 1000));
|
|
|
- // 🕯 🕯
|
|
|
- // 🕯 🕯
|
|
|
- // 🕯 🕯
|
|
|
-
|
|
|
- {
|
|
|
- const p = emittedOnce(ipcMain, 'onbeforeunload');
|
|
|
- w.close();
|
|
|
- const [e] = await p;
|
|
|
- e.returnValue = null;
|
|
|
- }
|
|
|
+ await w.webContents.executeJavaScript('installBeforeUnload(2)', true);
|
|
|
+ w.close();
|
|
|
+ await emittedOnce(w.webContents, 'before-unload-fired');
|
|
|
+ w.close();
|
|
|
+ await emittedOnce(w.webContents, 'before-unload-fired');
|
|
|
|
|
|
w.webContents.removeListener('destroyed', destroyListener);
|
|
|
- const p = emittedOnce(w.webContents, 'destroyed');
|
|
|
+ const wait = emittedOnce(w, 'closed');
|
|
|
w.close();
|
|
|
- await p;
|
|
|
+ await wait;
|
|
|
});
|
|
|
|
|
|
it('emits for each reload attempt', async () => {
|
|
@@ -2711,19 +2677,14 @@ describe('BrowserWindow module', () => {
|
|
|
const navigationListener = () => { expect.fail('Reload was not prevented'); };
|
|
|
w.webContents.once('did-start-navigation', navigationListener);
|
|
|
|
|
|
- await w.webContents.executeJavaScript('preventNextBeforeUnload()', true);
|
|
|
+ await w.webContents.executeJavaScript('installBeforeUnload(2)', true);
|
|
|
w.reload();
|
|
|
- {
|
|
|
- const [e] = await emittedOnce(ipcMain, 'onbeforeunload');
|
|
|
- e.returnValue = null;
|
|
|
- }
|
|
|
-
|
|
|
- await w.webContents.executeJavaScript('preventNextBeforeUnload()', true);
|
|
|
+ // Chromium does not emit 'before-unload-fired' on WebContents for
|
|
|
+ // navigations, so we have to use other ways to know if beforeunload
|
|
|
+ // is fired.
|
|
|
+ await emittedUntil(w.webContents, 'console-message', isBeforeUnload);
|
|
|
w.reload();
|
|
|
- {
|
|
|
- const [e] = await emittedOnce(ipcMain, 'onbeforeunload');
|
|
|
- e.returnValue = null;
|
|
|
- }
|
|
|
+ await emittedUntil(w.webContents, 'console-message', isBeforeUnload);
|
|
|
|
|
|
w.webContents.removeListener('did-start-navigation', navigationListener);
|
|
|
w.reload();
|
|
@@ -2736,19 +2697,14 @@ describe('BrowserWindow module', () => {
|
|
|
const navigationListener = () => { expect.fail('Reload was not prevented'); };
|
|
|
w.webContents.once('did-start-navigation', navigationListener);
|
|
|
|
|
|
- await w.webContents.executeJavaScript('preventNextBeforeUnload()', true);
|
|
|
+ await w.webContents.executeJavaScript('installBeforeUnload(2)', true);
|
|
|
w.loadURL('about:blank');
|
|
|
- {
|
|
|
- const [e] = await emittedOnce(ipcMain, 'onbeforeunload');
|
|
|
- e.returnValue = null;
|
|
|
- }
|
|
|
-
|
|
|
- await w.webContents.executeJavaScript('preventNextBeforeUnload()', true);
|
|
|
+ // Chromium does not emit 'before-unload-fired' on WebContents for
|
|
|
+ // navigations, so we have to use other ways to know if beforeunload
|
|
|
+ // is fired.
|
|
|
+ await emittedUntil(w.webContents, 'console-message', isBeforeUnload);
|
|
|
w.loadURL('about:blank');
|
|
|
- {
|
|
|
- const [e] = await emittedOnce(ipcMain, 'onbeforeunload');
|
|
|
- e.returnValue = null;
|
|
|
- }
|
|
|
+ await emittedUntil(w.webContents, 'console-message', isBeforeUnload);
|
|
|
|
|
|
w.webContents.removeListener('did-start-navigation', navigationListener);
|
|
|
w.loadURL('about:blank');
|