62b38812b6
* fix: invoke the window open handler for _blank links * add test
254 lines
9.2 KiB
TypeScript
254 lines
9.2 KiB
TypeScript
import { BrowserWindow } from 'electron';
|
|
import { writeFileSync, readFileSync } from 'fs';
|
|
import { resolve } from 'path';
|
|
import { expect, assert } from 'chai';
|
|
import { closeAllWindows } from './window-helpers';
|
|
const { emittedOnce } = require('./events-helpers');
|
|
|
|
function genSnapshot (browserWindow: BrowserWindow, features: string) {
|
|
return new Promise((resolve) => {
|
|
browserWindow.webContents.on('new-window', (...args: any[]) => {
|
|
resolve([features, ...args]);
|
|
});
|
|
browserWindow.webContents.executeJavaScript(`window.open('about:blank', 'frame-name', '${features}') && true`);
|
|
});
|
|
}
|
|
|
|
describe('new-window event', () => {
|
|
const testConfig = {
|
|
native: {
|
|
snapshotFileName: 'native-window-open.snapshot.txt',
|
|
browserWindowOptions: {
|
|
show: false,
|
|
width: 200,
|
|
title: 'cool',
|
|
backgroundColor: 'blue',
|
|
focusable: false,
|
|
webPreferences: {
|
|
nativeWindowOpen: true,
|
|
sandbox: true
|
|
}
|
|
}
|
|
},
|
|
proxy: {
|
|
snapshotFileName: 'proxy-window-open.snapshot.txt',
|
|
browserWindowOptions: {
|
|
show: false
|
|
}
|
|
}
|
|
};
|
|
|
|
for (const testName of Object.keys(testConfig) as (keyof typeof testConfig)[]) {
|
|
const { snapshotFileName, browserWindowOptions } = testConfig[testName];
|
|
|
|
describe(`for ${testName} window opening`, () => {
|
|
const snapshotFile = resolve(__dirname, 'fixtures', 'snapshots', snapshotFileName);
|
|
let browserWindow: BrowserWindow;
|
|
let existingSnapshots: any[];
|
|
|
|
before(() => {
|
|
existingSnapshots = parseSnapshots(readFileSync(snapshotFile, { encoding: 'utf8' }));
|
|
});
|
|
|
|
beforeEach((done) => {
|
|
browserWindow = new BrowserWindow(browserWindowOptions);
|
|
browserWindow.loadURL('about:blank');
|
|
browserWindow.on('ready-to-show', () => { done(); });
|
|
});
|
|
|
|
afterEach(closeAllWindows);
|
|
|
|
const newSnapshots: any[] = [];
|
|
[
|
|
'top=5,left=10,resizable=no',
|
|
'zoomFactor=2,resizable=0,x=0,y=10',
|
|
'backgroundColor=gray,webPreferences=0,x=100,y=100',
|
|
'x=50,y=20,title=sup',
|
|
'show=false,top=1,left=1'
|
|
].forEach((features, index) => {
|
|
/**
|
|
* ATTN: If this test is failing, you likely just need to change
|
|
* `shouldOverwriteSnapshot` to true and then evaluate the snapshot diff
|
|
* to see if the change is harmless.
|
|
*/
|
|
it(`matches snapshot for ${features}`, async () => {
|
|
const newSnapshot = await genSnapshot(browserWindow, features);
|
|
newSnapshots.push(newSnapshot);
|
|
// TODO: The output when these fail could be friendlier.
|
|
expect(stringifySnapshots(newSnapshot)).to.equal(stringifySnapshots(existingSnapshots[index]));
|
|
});
|
|
});
|
|
|
|
after(() => {
|
|
const shouldOverwriteSnapshot = false;
|
|
if (shouldOverwriteSnapshot) writeFileSync(snapshotFile, stringifySnapshots(newSnapshots, true));
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
describe('webContents.setWindowOpenHandler', () => {
|
|
const testConfig = {
|
|
native: {
|
|
browserWindowOptions: {
|
|
show: false,
|
|
webPreferences: {
|
|
nativeWindowOpen: true
|
|
}
|
|
}
|
|
},
|
|
proxy: {
|
|
browserWindowOptions: {
|
|
show: false,
|
|
webPreferences: {
|
|
nativeWindowOpen: false
|
|
}
|
|
}
|
|
}
|
|
};
|
|
|
|
for (const testName of Object.keys(testConfig) as (keyof typeof testConfig)[]) {
|
|
let browserWindow: BrowserWindow;
|
|
const { browserWindowOptions } = testConfig[testName];
|
|
|
|
describe(testName, () => {
|
|
beforeEach(async () => {
|
|
browserWindow = new BrowserWindow(browserWindowOptions);
|
|
await browserWindow.loadURL('about:blank');
|
|
browserWindow.show();
|
|
});
|
|
|
|
afterEach(closeAllWindows);
|
|
|
|
it('does not fire window creation events if an override returns action: deny', async () => {
|
|
const denied = new Promise((resolve) => {
|
|
browserWindow.webContents.setWindowOpenHandler(() => {
|
|
setTimeout(resolve);
|
|
return { action: 'deny' };
|
|
});
|
|
});
|
|
browserWindow.webContents.on('new-window', () => {
|
|
assert.fail('new-window should not to be called with an overridden window.open');
|
|
});
|
|
|
|
browserWindow.webContents.on('did-create-window', () => {
|
|
assert.fail('did-create-window should not to be called with an overridden window.open');
|
|
});
|
|
|
|
browserWindow.webContents.executeJavaScript("window.open('about:blank') && true");
|
|
|
|
await denied;
|
|
});
|
|
|
|
it('is called when clicking on a target=_blank link', async () => {
|
|
const denied = new Promise((resolve) => {
|
|
browserWindow.webContents.setWindowOpenHandler(() => {
|
|
setTimeout(resolve);
|
|
return { action: 'deny' };
|
|
});
|
|
});
|
|
browserWindow.webContents.on('new-window', () => {
|
|
assert.fail('new-window should not to be called with an overridden window.open');
|
|
});
|
|
|
|
browserWindow.webContents.on('did-create-window', () => {
|
|
assert.fail('did-create-window should not to be called with an overridden window.open');
|
|
});
|
|
|
|
await browserWindow.webContents.loadURL('data:text/html,<a target="_blank" href="http://example.com" style="display: block; width: 100%; height: 100%; position: fixed; left: 0; top: 0;">link</a>');
|
|
browserWindow.webContents.sendInputEvent({ type: 'mouseDown', x: 10, y: 10, button: 'left', clickCount: 1 });
|
|
browserWindow.webContents.sendInputEvent({ type: 'mouseUp', x: 10, y: 10, button: 'left', clickCount: 1 });
|
|
|
|
await denied;
|
|
});
|
|
|
|
it('is called when shift-clicking on a link', async () => {
|
|
const denied = new Promise((resolve) => {
|
|
browserWindow.webContents.setWindowOpenHandler(() => {
|
|
setTimeout(resolve);
|
|
return { action: 'deny' };
|
|
});
|
|
});
|
|
browserWindow.webContents.on('new-window', () => {
|
|
assert.fail('new-window should not to be called with an overridden window.open');
|
|
});
|
|
|
|
browserWindow.webContents.on('did-create-window', () => {
|
|
assert.fail('did-create-window should not to be called with an overridden window.open');
|
|
});
|
|
|
|
await browserWindow.webContents.loadURL('data:text/html,<a href="http://example.com" style="display: block; width: 100%; height: 100%; position: fixed; left: 0; top: 0;">link</a>');
|
|
browserWindow.webContents.sendInputEvent({ type: 'mouseDown', x: 10, y: 10, button: 'left', clickCount: 1, modifiers: ['shift'] });
|
|
browserWindow.webContents.sendInputEvent({ type: 'mouseUp', x: 10, y: 10, button: 'left', clickCount: 1, modifiers: ['shift'] });
|
|
|
|
await denied;
|
|
});
|
|
|
|
it('fires handler with correct params', (done) => {
|
|
const testFrameName = 'test-frame-name';
|
|
const testFeatures = 'top=10&left=10&something-unknown';
|
|
const testUrl = 'app://does-not-exist/';
|
|
browserWindow.webContents.setWindowOpenHandler(({ url, frameName, features }) => {
|
|
expect(url).to.equal(testUrl);
|
|
expect(frameName).to.equal(testFrameName);
|
|
expect(features).to.equal(testFeatures);
|
|
done();
|
|
return { action: 'deny' };
|
|
});
|
|
|
|
browserWindow.webContents.executeJavaScript(`window.open('${testUrl}', '${testFrameName}', '${testFeatures}') && true`);
|
|
});
|
|
|
|
it('does fire window creation events if an override returns action: allow', async () => {
|
|
browserWindow.webContents.setWindowOpenHandler(() => ({ action: 'allow' }));
|
|
|
|
setImmediate(() => {
|
|
browserWindow.webContents.executeJavaScript("window.open('about:blank') && true");
|
|
});
|
|
|
|
await Promise.all([
|
|
emittedOnce(browserWindow.webContents, 'did-create-window'),
|
|
emittedOnce(browserWindow.webContents, 'new-window')
|
|
]);
|
|
});
|
|
|
|
it('can change webPreferences of child windows', (done) => {
|
|
browserWindow.webContents.setWindowOpenHandler(() => ({ action: 'allow', overrideBrowserWindowOptions: { webPreferences: { defaultFontSize: 30 } } }));
|
|
|
|
browserWindow.webContents.on('did-create-window', async (childWindow) => {
|
|
await childWindow.webContents.executeJavaScript("document.write('hello')");
|
|
const size = await childWindow.webContents.executeJavaScript("getComputedStyle(document.querySelector('body')).fontSize");
|
|
expect(size).to.equal('30px');
|
|
done();
|
|
});
|
|
|
|
browserWindow.webContents.executeJavaScript("window.open('about:blank') && true");
|
|
});
|
|
});
|
|
}
|
|
});
|
|
|
|
function stringifySnapshots (snapshots: any, pretty = false) {
|
|
return JSON.stringify(snapshots, (key, value) => {
|
|
if (['sender', 'webContents'].includes(key)) {
|
|
return '[WebContents]';
|
|
}
|
|
if (key === 'openerId' && typeof value === 'number') {
|
|
return 'placeholder-opener-id';
|
|
}
|
|
if (key === 'processId' && typeof value === 'number') {
|
|
return 'placeholder-process-id';
|
|
}
|
|
if (key === 'returnValue') {
|
|
return 'placeholder-guest-contents-id';
|
|
}
|
|
return value;
|
|
}, pretty ? 2 : undefined);
|
|
}
|
|
|
|
function parseSnapshots (snapshotsJson: string) {
|
|
return JSON.parse(snapshotsJson, (key, value) => {
|
|
if (key === 'openerId' && value === 'placeholder-opener-id') return 1;
|
|
return value;
|
|
});
|
|
}
|