mirror of
https://github.com/electron/electron.git
synced 2026-05-02 03:00:22 -04:00
test: add linux coverage for default protocol client APIs (#51285)
Add Linux-only app tests to check the default protocol handler. This includes adding reusable XDG mock fixtures. Co-authored-by: trop[bot] <37223003+trop[bot]@users.noreply.github.com> Co-authored-by: Charles Kerr <charles@charleskerr.com>
This commit is contained in:
@@ -9,16 +9,32 @@ import * as fs from 'node:fs';
|
||||
import * as http from 'node:http';
|
||||
import * as https from 'node:https';
|
||||
import * as net from 'node:net';
|
||||
import * as os from 'node:os';
|
||||
import * as path from 'node:path';
|
||||
import * as readline from 'node:readline';
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import { promisify } from 'node:util';
|
||||
|
||||
import { collectStreamBody, getResponse } from './lib/net-helpers';
|
||||
import { ifdescribe, ifit, isWayland, listen, waitUntil } from './lib/spec-helpers';
|
||||
import { defer, ifdescribe, ifit, isWayland, listen, waitUntil } from './lib/spec-helpers';
|
||||
import { closeWindow, closeAllWindows } from './lib/window-helpers';
|
||||
|
||||
const fixturesPath = path.resolve(__dirname, 'fixtures');
|
||||
const xdgMockFixturePath = path.join(fixturesPath, 'api', 'xdg-mock');
|
||||
|
||||
function makeXdgMockDirectories (prefix: string) {
|
||||
const xdgDir = fs.mkdtempSync(path.join(os.tmpdir(), prefix));
|
||||
fs.cpSync(xdgMockFixturePath, xdgDir, { recursive: true });
|
||||
|
||||
const xdgDataHome = path.join(xdgDir, 'data');
|
||||
const xdgConfigHome = path.join(xdgDir, 'config');
|
||||
const xdgBinDir = path.join(xdgDir, 'bin');
|
||||
|
||||
fs.chmodSync(path.join(xdgBinDir, 'xdg-mime'), 0o755);
|
||||
fs.chmodSync(path.join(xdgBinDir, 'xdg-settings'), 0o755);
|
||||
|
||||
return { xdgDir, xdgDataHome, xdgConfigHome, xdgBinDir };
|
||||
}
|
||||
|
||||
const isMacOSx64 = process.platform === 'darwin' && process.arch === 'x64';
|
||||
|
||||
@@ -1458,7 +1474,6 @@ describe('app module', () => {
|
||||
const fixtureApp = path.join(fixturesPath, 'api', 'protocol-name');
|
||||
const desktopFileId = 'mock-browser.desktop';
|
||||
const mockScheme = 'mockproto';
|
||||
const mockMimeType = `x-scheme-handler/${mockScheme}`;
|
||||
|
||||
function spawnWithXdgMock (
|
||||
url: string,
|
||||
@@ -1507,62 +1522,7 @@ describe('app module', () => {
|
||||
let xdgConfigHome: string;
|
||||
let xdgBinDir: string;
|
||||
before(() => {
|
||||
xdgDir = fs.mkdtempSync(path.join(require('node:os').tmpdir(), 'electron-xdg-'));
|
||||
xdgDataHome = path.join(xdgDir, 'data');
|
||||
xdgConfigHome = path.join(xdgDir, 'config');
|
||||
xdgBinDir = path.join(xdgDir, 'bin');
|
||||
const appsDir = path.join(xdgDataHome, 'applications');
|
||||
fs.mkdirSync(appsDir, { recursive: true });
|
||||
fs.mkdirSync(xdgConfigHome, { recursive: true });
|
||||
fs.mkdirSync(xdgBinDir, { recursive: true });
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(appsDir, desktopFileId),
|
||||
[
|
||||
'[Desktop Entry]',
|
||||
'Name=Mock Browser',
|
||||
'Exec=/usr/bin/true %u',
|
||||
'Type=Application',
|
||||
`MimeType=${mockMimeType};`
|
||||
].join('\n')
|
||||
);
|
||||
|
||||
const mimeAppsContents = [
|
||||
'[Default Applications]',
|
||||
`${mockMimeType}=${desktopFileId}`,
|
||||
'',
|
||||
'[Added Associations]',
|
||||
`${mockMimeType}=${desktopFileId};`
|
||||
].join('\n');
|
||||
|
||||
fs.writeFileSync(path.join(xdgConfigHome, 'mimeapps.list'), mimeAppsContents);
|
||||
fs.writeFileSync(path.join(appsDir, 'mimeapps.list'), mimeAppsContents);
|
||||
fs.writeFileSync(path.join(appsDir, 'defaults.list'), mimeAppsContents);
|
||||
|
||||
// Different xdg-utils versions resolve custom XDG dirs differently, so
|
||||
// prepend a deterministic xdg-mime shim for this test.
|
||||
const xdgMimePath = path.join(xdgBinDir, 'xdg-mime');
|
||||
fs.writeFileSync(
|
||||
xdgMimePath,
|
||||
[
|
||||
'#!/bin/sh',
|
||||
'if [ "$1" != "query" ] || [ "$2" != "default" ]; then',
|
||||
' exit 1',
|
||||
'fi',
|
||||
'mime="$3"',
|
||||
'for list in "$XDG_CONFIG_HOME/mimeapps.list" "$XDG_DATA_HOME/applications/mimeapps.list" "$XDG_DATA_HOME/applications/defaults.list"; do',
|
||||
' if [ -f "$list" ]; then',
|
||||
' result=$(grep "^$mime=" "$list" | head -n 1 | cut -d= -f2 | cut -d";" -f1)',
|
||||
' if [ -n "$result" ]; then',
|
||||
' printf "%s\\n" "$result"',
|
||||
' exit 0',
|
||||
' fi',
|
||||
' fi',
|
||||
'done',
|
||||
'exit 0'
|
||||
].join('\n')
|
||||
);
|
||||
fs.chmodSync(xdgMimePath, 0o755);
|
||||
({ xdgDir, xdgDataHome, xdgConfigHome, xdgBinDir } = makeXdgMockDirectories('electron-xdg-'));
|
||||
});
|
||||
|
||||
after(() => {
|
||||
@@ -1604,6 +1564,95 @@ describe('app module', () => {
|
||||
});
|
||||
});
|
||||
|
||||
ifdescribe(process.platform === 'linux')('default protocol client APIs with mocked XDG settings', () => {
|
||||
const protocol = 'electron-test-linux';
|
||||
const desktopFileId = 'electron-test.desktop';
|
||||
const protocolMimeType = `x-scheme-handler/${protocol}`;
|
||||
|
||||
let xdgDir: string;
|
||||
let xdgDataHome: string;
|
||||
let xdgConfigHome: string;
|
||||
let xdgBinDir: string;
|
||||
let oldEnv: Record<string, string | undefined>;
|
||||
|
||||
const getRegisteredHandler = () => {
|
||||
for (const list of [
|
||||
path.join(xdgConfigHome, 'mimeapps.list'),
|
||||
path.join(xdgDataHome, 'applications', 'mimeapps.list'),
|
||||
path.join(xdgDataHome, 'applications', 'defaults.list')
|
||||
]) {
|
||||
if (!fs.existsSync(list)) continue;
|
||||
|
||||
const match = fs
|
||||
.readFileSync(list, 'utf8')
|
||||
.split('\n')
|
||||
.find((line) => line.startsWith(`${protocolMimeType}=`));
|
||||
|
||||
// foo=bar.desktop; --> bar.desktop
|
||||
if (match) return match.split('=', 2)[1].split(';', 1)[0];
|
||||
}
|
||||
|
||||
return '';
|
||||
};
|
||||
|
||||
beforeEach(() => {
|
||||
({ xdgDir, xdgDataHome, xdgConfigHome, xdgBinDir } = makeXdgMockDirectories('electron-xdg-default-client-'));
|
||||
|
||||
oldEnv = {
|
||||
PATH: process.env.PATH,
|
||||
CHROME_DESKTOP: process.env.CHROME_DESKTOP,
|
||||
XDG_DATA_HOME: process.env.XDG_DATA_HOME,
|
||||
XDG_DATA_DIRS: process.env.XDG_DATA_DIRS,
|
||||
XDG_CONFIG_HOME: process.env.XDG_CONFIG_HOME
|
||||
};
|
||||
|
||||
defer(() => {
|
||||
for (const [key, value] of Object.entries(oldEnv)) {
|
||||
if (value === undefined) {
|
||||
delete process.env[key];
|
||||
} else {
|
||||
process.env[key] = value;
|
||||
}
|
||||
}
|
||||
|
||||
fs.rmSync(xdgDir, { recursive: true, force: true });
|
||||
});
|
||||
|
||||
process.env.PATH = [xdgBinDir, oldEnv.PATH].filter(Boolean).join(':');
|
||||
process.env.XDG_DATA_HOME = xdgDataHome;
|
||||
process.env.XDG_DATA_DIRS = [xdgDataHome, oldEnv.XDG_DATA_DIRS].filter(Boolean).join(':');
|
||||
process.env.XDG_CONFIG_HOME = xdgConfigHome;
|
||||
app.setDesktopName(desktopFileId);
|
||||
});
|
||||
|
||||
it('writes the default handler to the XDG association files', async () => {
|
||||
expect(getRegisteredHandler()).to.equal('');
|
||||
|
||||
expect(app.setAsDefaultProtocolClient(protocol)).to.equal(true);
|
||||
|
||||
await waitUntil(() => getRegisteredHandler() === desktopFileId);
|
||||
expect(getRegisteredHandler()).to.equal(desktopFileId);
|
||||
});
|
||||
|
||||
it('detects whether the app is the default protocol client', async () => {
|
||||
expect(app.isDefaultProtocolClient(protocol)).to.equal(false);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(xdgConfigHome, 'mimeapps.list'),
|
||||
['[Default Applications]', `${protocolMimeType}=other.desktop`].join('\n')
|
||||
);
|
||||
expect(app.isDefaultProtocolClient(protocol)).to.equal(false);
|
||||
|
||||
fs.writeFileSync(
|
||||
path.join(xdgConfigHome, 'mimeapps.list'),
|
||||
['[Default Applications]', `${protocolMimeType}=${desktopFileId}`].join('\n')
|
||||
);
|
||||
|
||||
await waitUntil(() => app.isDefaultProtocolClient(protocol));
|
||||
expect(app.isDefaultProtocolClient(protocol)).to.equal(true);
|
||||
});
|
||||
});
|
||||
|
||||
describe('protocol scheme validation', () => {
|
||||
it('rejects empty protocol names', () => {
|
||||
expect(app.setAsDefaultProtocolClient('')).to.equal(false);
|
||||
|
||||
15
spec/fixtures/api/xdg-mock/bin/xdg-mime
vendored
Normal file
15
spec/fixtures/api/xdg-mock/bin/xdg-mime
vendored
Normal file
@@ -0,0 +1,15 @@
|
||||
#!/bin/sh
|
||||
if [ "$1" != "query" ] || [ "$2" != "default" ]; then
|
||||
exit 1
|
||||
fi
|
||||
mime="$3"
|
||||
for list in "$XDG_CONFIG_HOME/mimeapps.list" "$XDG_DATA_HOME/applications/mimeapps.list" "$XDG_DATA_HOME/applications/defaults.list"; do
|
||||
if [ -f "$list" ]; then
|
||||
result=$(grep "^$mime=" "$list" | head -n 1 | cut -d= -f2 | cut -d";" -f1)
|
||||
if [ -n "$result" ]; then
|
||||
printf "%s\n" "$result"
|
||||
exit 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
exit 0
|
||||
31
spec/fixtures/api/xdg-mock/bin/xdg-settings
vendored
Normal file
31
spec/fixtures/api/xdg-mock/bin/xdg-settings
vendored
Normal file
@@ -0,0 +1,31 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
get_handler() {
|
||||
for list in "$XDG_CONFIG_HOME/mimeapps.list" "$XDG_DATA_HOME/applications/mimeapps.list" "$XDG_DATA_HOME/applications/defaults.list"; do
|
||||
if [ -f "$list" ]; then
|
||||
result=$(grep "^x-scheme-handler/$1=" "$list" | head -n 1 | cut -d= -f2 | cut -d";" -f1)
|
||||
if [ -n "$result" ]; then
|
||||
printf "%s\n" "$result"
|
||||
return 0
|
||||
fi
|
||||
fi
|
||||
done
|
||||
return 1
|
||||
}
|
||||
if [ "$1" = "set" ] && [ "$2" = "default-url-scheme-handler" ]; then
|
||||
mkdir -p "$XDG_CONFIG_HOME"
|
||||
{
|
||||
printf "[Default Applications]\n"
|
||||
printf "x-scheme-handler/%s=%s\n" "$3" "$4"
|
||||
} > "$XDG_CONFIG_HOME/mimeapps.list"
|
||||
exit 0
|
||||
fi
|
||||
if [ "$1" = "check" ] && [ "$2" = "default-url-scheme-handler" ]; then
|
||||
if [ "$(get_handler "$3" 2>/dev/null || true)" = "$4" ]; then
|
||||
printf "yes\n"
|
||||
else
|
||||
printf "no\n"
|
||||
fi
|
||||
exit 0
|
||||
fi
|
||||
exit 1
|
||||
5
spec/fixtures/api/xdg-mock/config/mimeapps.list
vendored
Normal file
5
spec/fixtures/api/xdg-mock/config/mimeapps.list
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[Default Applications]
|
||||
x-scheme-handler/mockproto=mock-browser.desktop
|
||||
|
||||
[Added Associations]
|
||||
x-scheme-handler/mockproto=mock-browser.desktop;
|
||||
5
spec/fixtures/api/xdg-mock/data/applications/electron-test.desktop
vendored
Normal file
5
spec/fixtures/api/xdg-mock/data/applications/electron-test.desktop
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[Desktop Entry]
|
||||
Name=Electron Test
|
||||
Exec=/usr/bin/true %u
|
||||
Type=Application
|
||||
MimeType=x-scheme-handler/electron-test-linux;
|
||||
5
spec/fixtures/api/xdg-mock/data/applications/mock-browser.desktop
vendored
Normal file
5
spec/fixtures/api/xdg-mock/data/applications/mock-browser.desktop
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
[Desktop Entry]
|
||||
Name=Mock Browser
|
||||
Exec=/usr/bin/true %u
|
||||
Type=Application
|
||||
MimeType=x-scheme-handler/mockproto;
|
||||
Reference in New Issue
Block a user