diff --git a/spec/api-browser-window-spec.ts b/spec/api-browser-window.spec.ts
old mode 100755
new mode 100644
similarity index 96%
rename from spec/api-browser-window-spec.ts
rename to spec/api-browser-window.spec.ts
index 7f0bfec206..c7ad3f2403
--- a/spec/api-browser-window-spec.ts
+++ b/spec/api-browser-window.spec.ts
@@ -17,6 +17,7 @@ import {
} from 'electron/main';
import { expect } from 'chai';
+import { afterAll, afterEach, beforeAll, beforeEach, describe, it } from 'vitest';
import * as childProcess from 'node:child_process';
import { once } from 'node:events';
@@ -33,7 +34,7 @@ import * as nodeUrl from 'node:url';
import { emittedUntil, emittedNTimes } from './lib/events-helpers';
import { randomString } from './lib/net-helpers';
import { HexColors, hasCapturableScreen, ScreenCapture } from './lib/screen-helpers';
-import { ifit, ifdescribe, defer, listen, waitUntil, isWayland } from './lib/spec-helpers';
+import { ifit, ifdescribe, defer, listen, waitUntil, isWayland, withDone } from './lib/spec-helpers';
import { closeWindow, closeAllWindows } from './lib/window-helpers';
const fixtures = path.resolve(__dirname, 'fixtures');
@@ -193,7 +194,7 @@ describe('BrowserWindow module', () => {
let server: http.Server;
let url: string;
- before(async () => {
+ beforeAll(async () => {
server = http.createServer((request, response) => {
switch (request.url) {
case '/net-error':
@@ -220,7 +221,7 @@ describe('BrowserWindow module', () => {
url = (await listen(server)).url;
});
- after(() => {
+ afterAll(() => {
server.close();
});
@@ -381,14 +382,14 @@ describe('BrowserWindow module', () => {
let w: BrowserWindow;
const scheme = 'other';
const srcPath = path.join(fixtures, 'api');
- before(() => {
+ beforeAll(() => {
protocol.handle(scheme, (req) => {
const reqURL = new URL(req.url);
return net.fetch(nodeUrl.pathToFileURL(path.join(srcPath, reqURL.pathname)).toString());
});
});
- after(() => {
+ afterAll(() => {
protocol.unhandle(scheme);
});
@@ -402,7 +403,7 @@ describe('BrowserWindow module', () => {
let server: http.Server;
let url: string;
let postData = null as any;
- before(async () => {
+ beforeAll(async () => {
const filePath = path.join(fixtures, 'pages', 'a.html');
const fileStats = fs.statSync(filePath);
postData = [
@@ -448,7 +449,7 @@ describe('BrowserWindow module', () => {
url = (await listen(server)).url;
});
- after(() => {
+ afterAll(() => {
server.close();
});
@@ -495,13 +496,16 @@ describe('BrowserWindow module', () => {
const [, , , , isMainFrame] = await didFailLoad;
expect(isMainFrame).to.equal(false);
});
- it('does not crash in did-fail-provisional-load handler', (done) => {
- w.webContents.once('did-fail-provisional-load', () => {
+ it(
+ 'does not crash in did-fail-provisional-load handler',
+ withDone((done) => {
+ w.webContents.once('did-fail-provisional-load', () => {
+ w.loadURL('http://127.0.0.1:11111');
+ done();
+ });
w.loadURL('http://127.0.0.1:11111');
- done();
- });
- w.loadURL('http://127.0.0.1:11111');
- });
+ })
+ );
it('should emit did-fail-load event for URL exceeding character limit', async () => {
const data = Buffer.alloc(2 * 1024 * 1024).toString('base64');
const didFailLoad = once(w.webContents, 'did-fail-load');
@@ -665,7 +669,7 @@ describe('BrowserWindow module', () => {
describe('will-navigate event', () => {
let server: http.Server;
let url: string;
- before(async () => {
+ beforeAll(async () => {
server = http.createServer((req, res) => {
if (req.url === '/navigate-top') {
res.end('navigate _top');
@@ -676,7 +680,7 @@ describe('BrowserWindow module', () => {
url = (await listen(server)).url;
});
- after(() => {
+ afterAll(() => {
server.close();
});
@@ -687,25 +691,28 @@ describe('BrowserWindow module', () => {
w.close();
});
- it('can be prevented', (done) => {
- let willNavigate = false;
- w.webContents.once('will-navigate', (e) => {
- willNavigate = true;
- e.preventDefault();
- });
- w.webContents.on('did-stop-loading', () => {
- if (willNavigate) {
- // i.e. it shouldn't have had '?navigated' appended to it.
- try {
- expect(w.webContents.getURL().endsWith('will-navigate.html')).to.be.true();
- done();
- } catch (e) {
- done(e);
+ it(
+ 'can be prevented',
+ withDone((done) => {
+ let willNavigate = false;
+ w.webContents.once('will-navigate', (e) => {
+ willNavigate = true;
+ e.preventDefault();
+ });
+ w.webContents.on('did-stop-loading', () => {
+ if (willNavigate) {
+ // i.e. it shouldn't have had '?navigated' appended to it.
+ try {
+ expect(w.webContents.getURL().endsWith('will-navigate.html')).to.be.true();
+ done();
+ } catch (e) {
+ done(e);
+ }
}
- }
- });
- w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html'));
- });
+ });
+ w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html'));
+ })
+ );
it('is triggered when navigating from file: to http:', async () => {
await w.loadFile(path.join(fixtures, 'api', 'blank.html'));
@@ -773,7 +780,7 @@ describe('BrowserWindow module', () => {
describe('will-frame-navigate event', () => {
let server = null as unknown as http.Server;
let url = null as unknown as string;
- before(async () => {
+ beforeAll(async () => {
server = http.createServer((req, res) => {
if (req.url === '/navigate-top') {
res.end('navigate _top');
@@ -796,69 +803,78 @@ describe('BrowserWindow module', () => {
url = (await listen(server)).url;
});
- after(() => {
+ afterAll(() => {
server.close();
});
- it('allows the window to be closed from the event listener', (done) => {
- w.webContents.once('will-frame-navigate', () => {
- w.close();
- done();
- });
- w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html'));
- });
+ it(
+ 'allows the window to be closed from the event listener',
+ withDone((done) => {
+ w.webContents.once('will-frame-navigate', () => {
+ w.close();
+ done();
+ });
+ w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html'));
+ })
+ );
- it('can be prevented', (done) => {
- let willNavigate = false;
- w.webContents.once('will-frame-navigate', (e) => {
- willNavigate = true;
- e.preventDefault();
- });
- w.webContents.on('did-stop-loading', () => {
- if (willNavigate) {
- // i.e. it shouldn't have had '?navigated' appended to it.
- try {
- expect(w.webContents.getURL().endsWith('will-navigate.html')).to.be.true();
- done();
- } catch (e) {
- done(e);
- }
- }
- });
- w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html'));
- });
-
- it('can be prevented when navigating subframe', (done) => {
- let willNavigate = false;
- w.webContents.on(
- 'did-frame-navigate',
- (_event, _url, _httpResponseCode, _httpStatusText, isMainFrame, frameProcessId, frameRoutingId) => {
- if (isMainFrame) return;
-
- w.webContents.once('will-frame-navigate', (e) => {
- willNavigate = true;
- e.preventDefault();
- });
-
- w.webContents.on('did-stop-loading', () => {
- const frame = webFrameMain.fromId(frameProcessId, frameRoutingId);
- expect(frame).to.not.be.undefined();
- if (willNavigate) {
- // i.e. it shouldn't have had '?navigated' appended to it.
- try {
- expect(frame!.url.endsWith('/navigate-iframe-immediately')).to.be.true();
- done();
- } catch (e) {
- done(e);
- }
+ it(
+ 'can be prevented',
+ withDone((done) => {
+ let willNavigate = false;
+ w.webContents.once('will-frame-navigate', (e) => {
+ willNavigate = true;
+ e.preventDefault();
+ });
+ w.webContents.on('did-stop-loading', () => {
+ if (willNavigate) {
+ // i.e. it shouldn't have had '?navigated' appended to it.
+ try {
+ expect(w.webContents.getURL().endsWith('will-navigate.html')).to.be.true();
+ done();
+ } catch (e) {
+ done(e);
}
- });
- }
- );
- w.loadURL(
- `data:text/html,`
- );
- });
+ }
+ });
+ w.loadFile(path.join(fixtures, 'pages', 'will-navigate.html'));
+ })
+ );
+
+ it(
+ 'can be prevented when navigating subframe',
+ withDone((done) => {
+ let willNavigate = false;
+ w.webContents.on(
+ 'did-frame-navigate',
+ (_event, _url, _httpResponseCode, _httpStatusText, isMainFrame, frameProcessId, frameRoutingId) => {
+ if (isMainFrame) return;
+
+ w.webContents.once('will-frame-navigate', (e) => {
+ willNavigate = true;
+ e.preventDefault();
+ });
+
+ w.webContents.on('did-stop-loading', () => {
+ const frame = webFrameMain.fromId(frameProcessId, frameRoutingId);
+ expect(frame).to.not.be.undefined();
+ if (willNavigate) {
+ // i.e. it shouldn't have had '?navigated' appended to it.
+ try {
+ expect(frame!.url.endsWith('/navigate-iframe-immediately')).to.be.true();
+ done();
+ } catch (e) {
+ done(e);
+ }
+ }
+ });
+ }
+ );
+ w.loadURL(
+ `data:text/html,`
+ );
+ })
+ );
it('is triggered when navigating from file: to http:', async () => {
await w.loadFile(path.join(fixtures, 'api', 'blank.html'));
@@ -992,7 +1008,7 @@ describe('BrowserWindow module', () => {
describe('will-redirect event', () => {
let server: http.Server;
let url: string;
- before(async () => {
+ beforeAll(async () => {
server = http.createServer((req, res) => {
if (req.url === '/302') {
res.setHeader('Location', '/200');
@@ -1007,7 +1023,7 @@ describe('BrowserWindow module', () => {
url = (await listen(server)).url;
});
- after(() => {
+ afterAll(() => {
server.close();
});
it('is emitted on redirects', async () => {
@@ -1045,33 +1061,36 @@ describe('BrowserWindow module', () => {
w.close();
});
- it('can be prevented', (done) => {
- w.webContents.once('will-redirect', (event) => {
- event.preventDefault();
- });
- w.webContents.on('will-navigate', (e, u) => {
- expect(u).to.equal(`${url}/302`);
- });
- w.webContents.on('did-stop-loading', () => {
- try {
- expect(w.webContents.getURL()).to.equal(
- `${url}/navigate-302`,
- 'url should not have changed after navigation event'
- );
- done();
- } catch (e) {
- done(e);
- }
- });
- w.webContents.on('will-redirect', (e, u) => {
- try {
- expect(u).to.equal(`${url}/200`);
- } catch (e) {
- done(e);
- }
- });
- w.loadURL(`${url}/navigate-302`);
- });
+ it(
+ 'can be prevented',
+ withDone((done) => {
+ w.webContents.once('will-redirect', (event) => {
+ event.preventDefault();
+ });
+ w.webContents.on('will-navigate', (e, u) => {
+ expect(u).to.equal(`${url}/302`);
+ });
+ w.webContents.on('did-stop-loading', () => {
+ try {
+ expect(w.webContents.getURL()).to.equal(
+ `${url}/navigate-302`,
+ 'url should not have changed after navigation event'
+ );
+ done();
+ } catch (e) {
+ done(e);
+ }
+ });
+ w.webContents.on('will-redirect', (e, u) => {
+ try {
+ expect(u).to.equal(`${url}/200`);
+ } catch (e) {
+ done(e);
+ }
+ });
+ w.loadURL(`${url}/navigate-302`);
+ })
+ );
});
describe('ordering', () => {
@@ -1087,7 +1106,7 @@ describe('BrowserWindow module', () => {
'did-frame-navigate',
'did-navigate'
];
- before(async () => {
+ beforeAll(async () => {
server = http.createServer((req, res) => {
if (req.url === '/navigate') {
res.end('navigate');
@@ -1105,7 +1124,7 @@ describe('BrowserWindow module', () => {
});
url = (await listen(server)).url;
});
- after(() => {
+ afterAll(() => {
server.close();
});
it('for initial navigation, event order is consistent', async () => {
@@ -2137,43 +2156,46 @@ describe('BrowserWindow module', () => {
expect(w.isFullScreen()).to.equal(true);
});
- it('does not crash if maximized, minimized, then restored to maximized state', (done) => {
- w.destroy();
- w = new BrowserWindow({ show: false });
+ it(
+ 'does not crash if maximized, minimized, then restored to maximized state',
+ withDone((done) => {
+ w.destroy();
+ w = new BrowserWindow({ show: false });
- w.show();
+ w.show();
- let count = 0;
+ let count = 0;
- w.on('maximize', () => {
- if (count === 0) {
- syncSetTimeout(() => {
- w.minimize();
- });
- }
- count++;
- });
+ w.on('maximize', () => {
+ if (count === 0) {
+ syncSetTimeout(() => {
+ w.minimize();
+ });
+ }
+ count++;
+ });
- w.on('minimize', () => {
- if (count === 1) {
- syncSetTimeout(() => {
- w.restore();
- });
- }
- count++;
- });
+ w.on('minimize', () => {
+ if (count === 1) {
+ syncSetTimeout(() => {
+ w.restore();
+ });
+ }
+ count++;
+ });
- w.on('restore', () => {
- try {
- throw new Error('hey!');
- } catch (e: any) {
- expect(e.message).to.equal('hey!');
- done();
- }
- });
+ w.on('restore', () => {
+ try {
+ throw new Error('hey!');
+ } catch (e: any) {
+ expect(e.message).to.equal('hey!');
+ done();
+ }
+ });
- w.maximize();
- });
+ w.maximize();
+ })
+ );
it('checks normal bounds for maximized transparent window', async () => {
w.destroy();
@@ -2714,10 +2736,10 @@ describe('BrowserWindow module', () => {
describe('BrowserWindow.setProgressBar(progress)', () => {
let w: BrowserWindow;
- before(() => {
+ beforeAll(() => {
w = new BrowserWindow({ show: false });
});
- after(async () => {
+ afterAll(async () => {
await closeWindow(w);
w = null as unknown as BrowserWindow;
});
@@ -3975,7 +3997,7 @@ describe('BrowserWindow module', () => {
let server: http.Server;
let serverUrl: string;
- before(async () => {
+ beforeAll(async () => {
server = http.createServer((request, response) => {
switch (request.url) {
case '/cross-site':
@@ -3988,7 +4010,7 @@ describe('BrowserWindow module', () => {
serverUrl = (await listen(server)).url;
});
- after(() => {
+ afterAll(() => {
server.close();
});
@@ -4928,95 +4950,104 @@ describe('BrowserWindow module', () => {
});
describe('beginFrameSubscription method', () => {
- it('does not crash when callback returns nothing', (done) => {
- const w = new BrowserWindow({ show: false });
- let called = false;
- w.loadFile(path.join(fixtures, 'api', 'frame-subscriber.html'));
- w.webContents.on('dom-ready', async () => {
- await showWindowForWayland(w);
+ it(
+ 'does not crash when callback returns nothing',
+ withDone((done) => {
+ const w = new BrowserWindow({ show: false });
+ let called = false;
+ w.loadFile(path.join(fixtures, 'api', 'frame-subscriber.html'));
+ w.webContents.on('dom-ready', async () => {
+ await showWindowForWayland(w);
- w.webContents.beginFrameSubscription(function () {
- // This callback might be called twice.
- if (called) return;
- called = true;
+ w.webContents.beginFrameSubscription(function () {
+ // This callback might be called twice.
+ if (called) return;
+ called = true;
- // Pending endFrameSubscription to next tick can reliably reproduce
- // a crash which happens when nothing is returned in the callback.
- setTimeout().then(() => {
- w.webContents.endFrameSubscription();
- done();
+ // Pending endFrameSubscription to next tick can reliably reproduce
+ // a crash which happens when nothing is returned in the callback.
+ setTimeout().then(() => {
+ w.webContents.endFrameSubscription();
+ done();
+ });
});
});
- });
- });
+ })
+ );
- it('subscribes to frame updates', (done) => {
- const w = new BrowserWindow({ show: false });
- let called = false;
- w.loadFile(path.join(fixtures, 'api', 'frame-subscriber.html'));
- w.webContents.on('dom-ready', async () => {
- await showWindowForWayland(w);
+ it(
+ 'subscribes to frame updates',
+ withDone((done) => {
+ const w = new BrowserWindow({ show: false });
+ let called = false;
+ w.loadFile(path.join(fixtures, 'api', 'frame-subscriber.html'));
+ w.webContents.on('dom-ready', async () => {
+ await showWindowForWayland(w);
- w.webContents.beginFrameSubscription(function (data) {
- // This callback might be called twice.
- if (called) return;
- called = true;
+ w.webContents.beginFrameSubscription(function (data) {
+ // This callback might be called twice.
+ if (called) return;
+ called = true;
- try {
- expect(data.constructor.name).to.equal('NativeImage');
- expect(data.isEmpty()).to.be.false('data is empty');
- done();
- } catch (e) {
- done(e);
- } finally {
- w.webContents.endFrameSubscription();
- }
+ try {
+ expect(data.constructor.name).to.equal('NativeImage');
+ expect(data.isEmpty()).to.be.false('data is empty');
+ done();
+ } catch (e) {
+ done(e);
+ } finally {
+ w.webContents.endFrameSubscription();
+ }
+ });
});
- });
- });
+ })
+ );
- it('subscribes to frame updates (only dirty rectangle)', (done) => {
- const w = new BrowserWindow({ show: false });
- let called = false;
- let gotInitialFullSizeFrame = false;
- const [contentWidth, contentHeight] = w.getContentSize();
- w.webContents.on('did-finish-load', async () => {
- await showWindowForWayland(w);
+ it(
+ 'subscribes to frame updates (only dirty rectangle)',
+ withDone((done) => {
+ const w = new BrowserWindow({ show: false });
+ let called = false;
+ let gotInitialFullSizeFrame = false;
+ const [contentWidth, contentHeight] = w.getContentSize();
+ w.webContents.on('did-finish-load', async () => {
+ await showWindowForWayland(w);
- w.webContents.beginFrameSubscription(true, (image, rect) => {
- if (image.isEmpty()) {
- // Chromium sometimes sends a 0x0 frame at the beginning of the
- // page load.
- return;
- }
- if (rect.height === contentHeight && rect.width === contentWidth && !gotInitialFullSizeFrame) {
- // The initial frame is full-size, but we're looking for a call
- // with just the dirty-rect. The next frame should be a smaller
- // rect.
- gotInitialFullSizeFrame = true;
- return;
- }
- // This callback might be called twice.
- if (called) return;
- // We asked for just the dirty rectangle, so we expect to receive a
- // rect smaller than the full size.
- // TODO(jeremy): this is failing on windows currently; investigate.
- // assert(rect.width < contentWidth || rect.height < contentHeight)
- called = true;
+ w.webContents.beginFrameSubscription(true, (image, rect) => {
+ if (image.isEmpty()) {
+ // Chromium sometimes sends a 0x0 frame at the beginning of the
+ // page load.
+ return;
+ }
+ if (rect.height === contentHeight && rect.width === contentWidth && !gotInitialFullSizeFrame) {
+ // The initial frame is full-size, but we're looking for a call
+ // with just the dirty-rect. The next frame should be a smaller
+ // rect.
+ gotInitialFullSizeFrame = true;
+ return;
+ }
+ // This callback might be called twice.
+ if (called) return;
+ // We asked for just the dirty rectangle, so we expect to receive a
+ // rect smaller than the full size.
+ // TODO(jeremy): this is failing on windows currently; investigate.
+ // assert(rect.width < contentWidth || rect.height < contentHeight)
+ called = true;
- try {
- const expectedSize = rect.width * rect.height * 4;
- expect(image.toBitmap()).to.be.an.instanceOf(Buffer).with.lengthOf(expectedSize);
- done();
- } catch (e) {
- done(e);
- } finally {
- w.webContents.endFrameSubscription();
- }
+ try {
+ const expectedSize = rect.width * rect.height * 4;
+ expect(image.toBitmap()).to.be.an.instanceOf(Buffer).with.lengthOf(expectedSize);
+ done();
+ } catch (e) {
+ done(e);
+ } finally {
+ w.webContents.endFrameSubscription();
+ }
+ });
});
- });
- w.loadFile(path.join(fixtures, 'api', 'frame-subscriber.html'));
- });
+ w.loadFile(path.join(fixtures, 'api', 'frame-subscriber.html'));
+ })
+ );
it('throws error when subscriber is not well defined', () => {
const w = new BrowserWindow({ show: false });
@@ -5364,17 +5395,20 @@ describe('BrowserWindow module', () => {
expect(w.getChildWindows().length).to.equal(0);
});
- it('can handle parent window close with focus or blur events', (done) => {
- const w = new BrowserWindow({ show: false });
- const c = new BrowserWindow({ show: false, parent: w });
+ it(
+ 'can handle parent window close with focus or blur events',
+ withDone((done) => {
+ const w = new BrowserWindow({ show: false });
+ const c = new BrowserWindow({ show: false, parent: w });
- c.on('closed', () => {
- w.focus();
- done();
- });
+ c.on('closed', () => {
+ w.focus();
+ done();
+ });
- w.close();
- });
+ w.close();
+ })
+ );
ifit(process.platform === 'darwin')(
'only shows the intended window when a child with siblings is shown',
@@ -5793,14 +5827,14 @@ describe('BrowserWindow module', () => {
let server: http.Server;
let serverUrl: string;
- before(async () => {
+ beforeAll(async () => {
server = http.createServer((request, response) => {
response.end();
});
serverUrl = (await listen(server)).url;
});
- after(() => {
+ afterAll(() => {
server.close();
});
@@ -6980,28 +7014,31 @@ describe('BrowserWindow module', () => {
}
);
- it('reloading does not cause Node.js module API hangs after reload', (done) => {
- const w = new BrowserWindow({
- show: false,
- webPreferences: {
- nodeIntegration: true,
- contextIsolation: false
- }
- });
+ it(
+ 'reloading does not cause Node.js module API hangs after reload',
+ withDone((done) => {
+ const w = new BrowserWindow({
+ show: false,
+ webPreferences: {
+ nodeIntegration: true,
+ contextIsolation: false
+ }
+ });
- let count = 0;
- ipcMain.on('async-node-api-done', () => {
- if (count === 3) {
- ipcMain.removeAllListeners('async-node-api-done');
- done();
- } else {
- count++;
- w.reload();
- }
- });
+ let count = 0;
+ ipcMain.on('async-node-api-done', () => {
+ if (count === 3) {
+ ipcMain.removeAllListeners('async-node-api-done');
+ done();
+ } else {
+ count++;
+ w.reload();
+ }
+ });
- w.loadFile(path.join(fixtures, 'pages', 'send-after-node.html'));
- });
+ w.loadFile(path.join(fixtures, 'pages', 'send-after-node.html'));
+ })
+ );
// TODO(codebytere): fix on Windows and Linux too
ifdescribe(process.platform === 'darwin')('window.webContents initial paint', () => {