test: Added download test and made app more testable in general

fix: Store Downloads not properly working on steam deck
fix: Removed linux shortcuts implementation
This commit is contained in:
Simeon Radivoev 2026-03-31 03:11:02 +03:00
parent 58d3c31c56
commit 8a0be8c913
Signed by: simeonradivoev
GPG key ID: C16C2132A7660C8E
26 changed files with 422 additions and 210 deletions

View file

@ -22,7 +22,7 @@ export class BiosDownloadJob implements IJob<z.infer<typeof BiosDownloadJob.data
this.dryRun = init?.dryRun ?? false;
}
async start (context: JobContext<IJob<never, "download">, never, "download">)
async start (context: JobContext<IJob<z.infer<typeof BiosDownloadJob.dataSchema>, "download">, z.infer<typeof BiosDownloadJob.dataSchema>, "download">)
{
const emulator = await getStoreEmulatorPackage(this.emulator);
if (!emulator) throw new Error("Could Not Find Emulator");

View file

@ -40,25 +40,27 @@ export class EmulatorDownloadJob implements IJob<z.infer<typeof EmulatorDownload
if (!validDownloads) throw new Error(`Now downloads in ${this.emulatorPackage.name} for platform ${process.platform}:${process.arch}`);
const validDownload = validDownloads.find(d => d.type === this.downloadSource);
if (!validDownload || !validDownload.path) throw new Error(`Download type ${this.downloadSource} not found`);
if (!validDownload) throw new Error(`Download type ${this.downloadSource} not found`);
console.log("Trying To Download from ", `https://api.github.com/repos/${validDownload.path}/releases/latest`);
const latestRelease = await getOrCachedGithubRelease(validDownload.path);
const glob = new Glob(validDownload.pattern);
const validAsset = latestRelease.assets.find(a => glob.match(a.name));
if (!validAsset) throw new Error("Could Not Find Valid Asset");
const downloadUrl = validAsset.browser_download_url;
const emulatorsFolder = path.join(config.get('downloadPath'), "emulators", this.emulator);
const isArchive = validAsset.content_type === 'application/x-7z-compressed' || validAsset.name.endsWith('.7z') || validAsset.content_type === 'application/zip' || validAsset.name.endsWith('.zip');
const isAppImage = validAsset.name.endsWith(".AppImage");
if (!isArchive && !isAppImage)
let downloadUrl: URL;
if (validDownload.type === 'github')
{
throw new Error("Invalid Download Type");
console.log("Trying To Download from ", `https://api.github.com/repos/${validDownload.path}/releases/latest`);
const latestRelease = await getOrCachedGithubRelease(validDownload.path);
const glob = new Glob(validDownload.pattern);
const validAsset = latestRelease.assets.find(a => glob.match(a.name));
if (!validAsset) throw new Error("Could Not Find Valid Asset");
downloadUrl = new URL(validAsset.browser_download_url);
} else if (validDownload.type === 'direct')
{
downloadUrl = new URL(validDownload.url);
} else
{
throw new Error("Download Type Unsupported");
}
const emulatorsFolder = path.join(config.get('downloadPath'), "emulators", this.emulator);
if (this.dryRun)
{
await simulateProgress(p => context.setProgress(p, "download"), context.abortSignal);
@ -67,7 +69,7 @@ export class EmulatorDownloadJob implements IJob<z.infer<typeof EmulatorDownload
{
const tmpFolder = path.join(config.get("downloadPath"), ".tmp");
const downloader = new Downloader(this.emulator,
[{ url: new URL(downloadUrl), file_name: path.basename(downloadUrl), file_path: this.emulator }],
[{ url: new URL(downloadUrl), file_name: path.basename(downloadUrl.pathname), file_path: this.emulator }],
tmpFolder,
{
signal: context.abortSignal,
@ -80,6 +82,14 @@ export class EmulatorDownloadJob implements IJob<z.infer<typeof EmulatorDownload
const destinationPaths = await downloader.start();
if (destinationPaths)
{
const isArchive = destinationPaths[0].endsWith('.7z') || destinationPaths[0].endsWith('.zip');
const isAppImage = destinationPaths[0].endsWith(".AppImage");
if (!isArchive && !isAppImage)
{
throw new Error("Invalid Download Type");
}
if (isArchive)
{
if (destinationPaths[0])

View file

@ -106,15 +106,23 @@ export class InstallJob implements IJob<never, InstallJobStates>
{
let progress = 0;
const progressDelta = 1 / downloadedFiles.length;
for (const path of downloadedFiles)
for (const filePath of downloadedFiles)
{
const extractPath = info.extract_path;
const extractPath = path.join(config.get('downloadPath'), info.extract_path);
await new Promise((resolve, reject) =>
{
const seven = Seven.extractFull(path, extractPath, { $bin: process.env.ZIP7_PATH, $progress: true });
seven.on('progress', p => cx.setProgress(progress + p.percent * progressDelta, "extract"));
const seven = Seven.extractFull(filePath, extractPath, { $bin: process.env.ZIP7_PATH, $progress: true });
seven.on('progress', p =>
{
cx.setProgress(progress + p.percent * progressDelta, "extract");
});
seven.on('error', e => reject(e));
seven.on('end', () => resolve(true));
seven.on('end', async () =>
{
await fs.rm(filePath);
resolve(true);
});
});
progress += progressDelta * 100;
}

View file

@ -25,7 +25,7 @@ export class LaunchGameJob implements IJob<z.infer<typeof LaunchGameJob.dataSche
this.gameSourceId = sourceId;
}
async start (context: JobContext<IJob<ActiveGameType, "playing">, ActiveGameType, "playing">)
async start (context: JobContext<IJob<z.infer<typeof LaunchGameJob.dataSchema>, "playing">, z.infer<typeof LaunchGameJob.dataSchema>, "playing">)
{
const localGame = await db.query.games.findFirst({
where: eq(appSchema.games.id, this.gameId), columns: {