mirror of
https://gitee.com/spark-store-project/spark-store
synced 2026-04-26 01:10:16 +08:00
255 lines
8.1 KiB
TypeScript
255 lines
8.1 KiB
TypeScript
import { afterEach, describe, expect, it, vi } from "vitest";
|
|
|
|
type FsState = {
|
|
directories?: Record<string, string[]>;
|
|
files?: Record<string, string>;
|
|
packageFiles?: Record<string, string[]>;
|
|
};
|
|
|
|
const loadIconsModule = async (state: FsState) => {
|
|
vi.resetModules();
|
|
|
|
vi.doMock("node:fs", () => {
|
|
const directories = state.directories ?? {};
|
|
const files = state.files ?? {};
|
|
|
|
const existsSync = (targetPath: string): boolean => {
|
|
return targetPath in directories || targetPath in files;
|
|
};
|
|
|
|
const readdirSync = (targetPath: string): string[] => {
|
|
return directories[targetPath] ?? [];
|
|
};
|
|
|
|
const readFileSync = (targetPath: string): string => {
|
|
const content = files[targetPath];
|
|
if (content === undefined) {
|
|
throw new Error(`Unexpected read for ${targetPath}`);
|
|
}
|
|
|
|
return content;
|
|
};
|
|
|
|
return {
|
|
default: {
|
|
existsSync,
|
|
readdirSync,
|
|
readFileSync,
|
|
},
|
|
existsSync,
|
|
readdirSync,
|
|
readFileSync,
|
|
};
|
|
});
|
|
|
|
vi.doMock("node:child_process", () => {
|
|
const packageFiles = state.packageFiles ?? {};
|
|
|
|
const spawnSync = (_command: string, args: string[]) => {
|
|
const operation = args[0] ?? "";
|
|
const pkgname = args[1] ?? "";
|
|
const ownedFiles = packageFiles[pkgname];
|
|
if (operation !== "-L" || !ownedFiles) {
|
|
return {
|
|
status: 1,
|
|
error: undefined,
|
|
output: null,
|
|
pid: 0,
|
|
signal: null,
|
|
stdout: Buffer.alloc(0),
|
|
stderr: Buffer.alloc(0),
|
|
};
|
|
}
|
|
|
|
return {
|
|
status: 0,
|
|
error: undefined,
|
|
output: null,
|
|
pid: 0,
|
|
signal: null,
|
|
stdout: Buffer.from(`${ownedFiles.join("\n")}\n`),
|
|
stderr: Buffer.alloc(0),
|
|
};
|
|
};
|
|
|
|
return {
|
|
default: { spawnSync },
|
|
spawnSync,
|
|
};
|
|
});
|
|
|
|
return await import("../../../../electron/main/backend/update-center/icons");
|
|
};
|
|
|
|
afterEach(() => {
|
|
vi.doUnmock("node:fs");
|
|
vi.doUnmock("node:child_process");
|
|
});
|
|
|
|
describe("update-center icons", () => {
|
|
it("prefers local desktop icon paths for aptss items", async () => {
|
|
const pkgname = "spark-weather";
|
|
const applicationsDirectory = "/usr/share/applications";
|
|
const desktopPath = `${applicationsDirectory}/weather-launcher.desktop`;
|
|
const iconPath = `/usr/share/pixmaps/${pkgname}.png`;
|
|
const { resolveUpdateItemIcon } = await loadIconsModule({
|
|
directories: {
|
|
[applicationsDirectory]: ["weather-launcher.desktop"],
|
|
},
|
|
files: {
|
|
[desktopPath]: `[Desktop Entry]\nName=Spark Weather\nIcon=${iconPath}\n`,
|
|
[iconPath]: "png",
|
|
},
|
|
packageFiles: {
|
|
[pkgname]: [desktopPath],
|
|
},
|
|
});
|
|
|
|
expect(
|
|
resolveUpdateItemIcon({
|
|
pkgname,
|
|
source: "aptss",
|
|
currentVersion: "1.0.0",
|
|
nextVersion: "2.0.0",
|
|
}),
|
|
).toBe(iconPath);
|
|
});
|
|
|
|
it("resolves APM icon names from entries/icons when desktop icon is not absolute", async () => {
|
|
const pkgname = "spark-music";
|
|
const desktopDirectory = `/var/lib/apm/apm/files/ace-env/var/lib/apm/${pkgname}/entries/applications`;
|
|
const desktopPath = `${desktopDirectory}/${pkgname}.desktop`;
|
|
const iconPath = `/var/lib/apm/apm/files/ace-env/var/lib/apm/${pkgname}/entries/icons/hicolor/48x48/apps/${pkgname}.png`;
|
|
const { resolveUpdateItemIcon } = await loadIconsModule({
|
|
directories: {
|
|
[desktopDirectory]: [`${pkgname}.desktop`],
|
|
},
|
|
files: {
|
|
[desktopPath]: `[Desktop Entry]\nName=Spark Music\nIcon=${pkgname}\n`,
|
|
[iconPath]: "png",
|
|
},
|
|
});
|
|
|
|
expect(
|
|
resolveUpdateItemIcon({
|
|
pkgname,
|
|
source: "apm",
|
|
currentVersion: "1.0.0",
|
|
nextVersion: "2.0.0",
|
|
}),
|
|
).toBe(iconPath);
|
|
});
|
|
|
|
it("checks later APM desktop entries when the first one has no usable icon", async () => {
|
|
const pkgname = "spark-player";
|
|
const desktopDirectory = `/var/lib/apm/apm/files/ace-env/var/lib/apm/${pkgname}/entries/applications`;
|
|
const invalidDesktopPath = `${desktopDirectory}/invalid.desktop`;
|
|
const validDesktopPath = `${desktopDirectory}/valid.desktop`;
|
|
const iconPath = `/var/lib/apm/apm/files/ace-env/var/lib/apm/${pkgname}/entries/icons/hicolor/48x48/apps/${pkgname}.png`;
|
|
const { resolveApmIcon } = await loadIconsModule({
|
|
directories: {
|
|
[desktopDirectory]: ["invalid.desktop", "valid.desktop"],
|
|
},
|
|
files: {
|
|
[invalidDesktopPath]:
|
|
"[Desktop Entry]\nName=Invalid\nIcon=missing-icon\n",
|
|
[validDesktopPath]: `[Desktop Entry]\nName=Spark Player\nIcon=${pkgname}\n`,
|
|
[iconPath]: "png",
|
|
},
|
|
});
|
|
|
|
expect(resolveApmIcon(pkgname)).toBe(iconPath);
|
|
});
|
|
|
|
it("resolves APM icons from installed /opt/apps entries when package-path assets are absent", async () => {
|
|
const pkgname = "spark-video";
|
|
const installedDesktopDirectory = `/opt/apps/${pkgname}/entries/applications`;
|
|
const installedDesktopPath = `${installedDesktopDirectory}/${pkgname}.desktop`;
|
|
const installedIconPath = `/opt/apps/${pkgname}/entries/icons/hicolor/48x48/apps/${pkgname}.png`;
|
|
const { resolveApmIcon } = await loadIconsModule({
|
|
directories: {
|
|
[installedDesktopDirectory]: [`${pkgname}.desktop`],
|
|
},
|
|
files: {
|
|
[installedDesktopPath]: `[Desktop Entry]\nName=Spark Video\nIcon=${pkgname}\n`,
|
|
[installedIconPath]: "png",
|
|
},
|
|
});
|
|
|
|
expect(resolveApmIcon(pkgname)).toBe(installedIconPath);
|
|
});
|
|
|
|
it("resolves APM named icons from shared theme locations when local entries icons are absent", async () => {
|
|
const pkgname = "spark-camera";
|
|
const desktopDirectory = `/var/lib/apm/apm/files/ace-env/var/lib/apm/${pkgname}/entries/applications`;
|
|
const desktopPath = `${desktopDirectory}/${pkgname}.desktop`;
|
|
const sharedIconPath = `/usr/share/icons/hicolor/48x48/apps/${pkgname}.png`;
|
|
const { resolveApmIcon } = await loadIconsModule({
|
|
directories: {
|
|
[desktopDirectory]: [`${pkgname}.desktop`],
|
|
},
|
|
files: {
|
|
[desktopPath]: `[Desktop Entry]\nName=Spark Camera\nIcon=${pkgname}\n`,
|
|
[sharedIconPath]: "png",
|
|
},
|
|
});
|
|
|
|
expect(resolveApmIcon(pkgname)).toBe(sharedIconPath);
|
|
});
|
|
|
|
it("builds a remote fallback URL when category and arch are available", async () => {
|
|
const { resolveUpdateItemIcon } = await loadIconsModule({});
|
|
|
|
expect(
|
|
resolveUpdateItemIcon({
|
|
pkgname: "spark-clock",
|
|
source: "apm",
|
|
currentVersion: "1.0.0",
|
|
nextVersion: "2.0.0",
|
|
category: "utility",
|
|
arch: "amd64",
|
|
}),
|
|
).toBe(
|
|
"https://erotica.spark-app.store/amd64-apm/utility/spark-clock/icon.png",
|
|
);
|
|
});
|
|
|
|
it("returns empty string when neither local nor remote icon can be determined", async () => {
|
|
const { resolveUpdateItemIcon } = await loadIconsModule({});
|
|
|
|
expect(
|
|
resolveUpdateItemIcon({
|
|
pkgname: "spark-empty",
|
|
source: "aptss",
|
|
currentVersion: "1.0.0",
|
|
nextVersion: "2.0.0",
|
|
}),
|
|
).toBe("");
|
|
});
|
|
|
|
it("ignores unrelated desktop files while still resolving owned non-exact filenames", async () => {
|
|
const pkgname = "spark-reader";
|
|
const applicationsDirectory = "/usr/share/applications";
|
|
const unrelatedDesktopPath = `${applicationsDirectory}/notes.desktop`;
|
|
const ownedDesktopPath = `${applicationsDirectory}/reader-launcher.desktop`;
|
|
const unrelatedIconPath = "/usr/share/pixmaps/notes.png";
|
|
const ownedIconPath = `/usr/share/pixmaps/${pkgname}.png`;
|
|
const { resolveDesktopIcon } = await loadIconsModule({
|
|
directories: {
|
|
[applicationsDirectory]: ["notes.desktop", "reader-launcher.desktop"],
|
|
},
|
|
files: {
|
|
[unrelatedDesktopPath]: `[Desktop Entry]\nName=Notes\nIcon=${unrelatedIconPath}\n`,
|
|
[ownedDesktopPath]: `[Desktop Entry]\nName=Spark Reader\nIcon=${ownedIconPath}\n`,
|
|
[unrelatedIconPath]: "png",
|
|
[ownedIconPath]: "png",
|
|
},
|
|
packageFiles: {
|
|
[pkgname]: [ownedDesktopPath],
|
|
},
|
|
});
|
|
|
|
expect(resolveDesktopIcon(pkgname)).toBe(ownedIconPath);
|
|
});
|
|
});
|