test: add linux coverage for default protocol client APIs (#51288)

Add Linux-only app tests to check the default protocol handler.
This includes adding reusable XDG mock fixtures.

Manual backport of 2c46abe from `main`.
This commit is contained in:
Charles Kerr
2026-04-23 13:47:37 -05:00
committed by GitHub
parent f24e43dc75
commit c4965cb580
6 changed files with 166 additions and 1 deletions

View File

@@ -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, listen, waitUntil } from './lib/spec-helpers';
import { defer, ifdescribe, ifit, 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';
@@ -1435,6 +1451,94 @@ 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}=`));
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('getApplicationNameForProtocol()', () => {
// TODO: Linux CI doesn't have registered http & https handlers
ifit(!(process.env.CI && process.platform === 'linux'))('returns application names for common protocols', function () {

15
spec/fixtures/api/xdg-mock/bin/xdg-mime vendored Normal file
View 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

View 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

View File

@@ -0,0 +1,5 @@
[Default Applications]
x-scheme-handler/mockproto=mock-browser.desktop
[Added Associations]
x-scheme-handler/mockproto=mock-browser.desktop;

View File

@@ -0,0 +1,5 @@
[Desktop Entry]
Name=Electron Test
Exec=/usr/bin/true %u
Type=Application
MimeType=x-scheme-handler/electron-test-linux;

View File

@@ -0,0 +1,5 @@
[Desktop Entry]
Name=Mock Browser
Exec=/usr/bin/true %u
Type=Application
MimeType=x-scheme-handler/mockproto;