import { expect } from 'chai'; import * as cp from 'node:child_process'; import * as fs from 'node:fs'; import * as path from 'node:path'; const fixturesPath = path.resolve(__dirname, '..', 'fixtures', 'api', 'autoupdater', 'msix'); const manifestFixturePath = path.resolve(fixturesPath, 'ElectronDevAppxManifest.xml'); const installCertScriptPath = path.resolve(fixturesPath, 'install_test_cert.ps1'); // Install the signing certificate for MSIX test packages to the Trusted People store // This is required to install self-signed MSIX packages export async function installMsixCertificate (): Promise { const result = cp.spawnSync('powershell', [ '-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', installCertScriptPath ]); if (result.status !== 0) { throw new Error(`Failed to install MSIX certificate: ${result.stderr.toString() || result.stdout.toString()}`); } } // Check if we should run MSIX tests export const shouldRunMsixTests = process.platform === 'win32'; // Get the Electron executable path export function getElectronExecutable (): string { return process.execPath; } // Get path to main.js fixture export function getMainJsFixturePath (): string { return path.resolve(fixturesPath, 'main.js'); } // Register executable with identity export async function registerExecutableWithIdentity (executablePath: string): Promise { if (!fs.existsSync(manifestFixturePath)) { throw new Error(`Manifest fixture not found: ${manifestFixturePath}`); } const executableDir = path.dirname(executablePath); const manifestPath = path.join(executableDir, 'AppxManifest.xml'); const escapedManifestPath = manifestPath.replace(/'/g, "''").replace(/\\/g, '\\\\'); const psCommand = ` $ErrorActionPreference = 'Stop'; try { Add-AppxPackage -Register '${escapedManifestPath}' -ForceUpdateFromAnyVersion } catch { Write-Error $_.Exception.Message exit 1 } `; fs.copyFileSync(manifestFixturePath, manifestPath); const result = cp.spawnSync('powershell', ['-NoProfile', '-Command', psCommand]); if (result.status !== 0) { const errorMsg = result.stderr.toString() || result.stdout.toString(); try { fs.unlinkSync(manifestPath); } catch { // Ignore cleanup errors } throw new Error(`Failed to register executable with identity: ${errorMsg}`); } } // Unregister the Electron Dev MSIX package // This removes the sparse package registration created by registerExecutableWithIdentity export async function unregisterExecutableWithIdentity (): Promise { const result = cp.spawnSync('powershell', ['-NoProfile', '-Command', ' Get-AppxPackage Electron.Dev.MSIX | Remove-AppxPackage']); // Don't throw if package doesn't exist if (result.status !== 0) { throw new Error(`Failed to unregister executable with identity: ${result.stderr.toString() || result.stdout.toString()}`); } const electronExec = getElectronExecutable(); const executableDir = path.dirname(electronExec); const manifestPath = path.join(executableDir, 'AppxManifest.xml'); try { if (fs.existsSync(manifestPath)) { fs.unlinkSync(manifestPath); } } catch { // Ignore cleanup errors } } // Get path to MSIX fixture package export function getMsixFixturePath (version: 'v1' | 'v2'): string { const filename = `HelloMSIX_${version}.msix`; return path.resolve(fixturesPath, filename); } // Install MSIX package export async function installMsixPackage (msixPath: string): Promise { // Use Add-AppxPackage PowerShell cmdlet const result = cp.spawnSync('powershell', [ '-Command', `Add-AppxPackage -Path "${msixPath}" -ForceApplicationShutdown` ]); if (result.status !== 0) { throw new Error(`Failed to install MSIX package: ${result.stderr.toString()}`); } } // Uninstall MSIX package by name export async function uninstallMsixPackage (name: string): Promise { const result = cp.spawnSync('powershell', ['-NoProfile', '-Command', `Get-AppxPackage ${name} | Remove-AppxPackage`]); // Don't throw if package doesn't exist if (result.status !== 0) { throw new Error(`Failed to uninstall MSIX package: ${result.stderr.toString() || result.stdout.toString()}`); } } // Get version of installed MSIX package by name export async function getMsixPackageVersion (name: string): Promise { const psCommand = `(Get-AppxPackage -Name '${name}').Version`; const result = cp.spawnSync('powershell', ['-NoProfile', '-Command', psCommand]); if (result.status !== 0) { return null; } const version = result.stdout.toString().trim(); return version || null; } export function spawn (cmd: string, args: string[], opts: any = {}): Promise<{ code: number, out: string }> { let out = ''; const child = cp.spawn(cmd, args, opts); child.stdout.on('data', (chunk: Buffer) => { out += chunk.toString(); }); child.stderr.on('data', (chunk: Buffer) => { out += chunk.toString(); }); return new Promise<{ code: number, out: string }>((resolve) => { child.on('exit', (code, signal) => { expect(signal).to.equal(null); resolve({ code: code!, out }); }); }); }