diff --git a/jest.setup.ts b/jest.setup.ts index 9fdaeb8..296a148 100644 --- a/jest.setup.ts +++ b/jest.setup.ts @@ -1,10 +1,10 @@ import * as fs from 'fs-extra'; import * as path from 'path'; -import { appsPath, asarsDir, templateApp } from './test/util'; +import { appsDir, asarsDir, templateApp } from './test/util'; export default async () => { - await fs.remove(appsPath); - await fs.mkdirp(appsPath); + await fs.remove(appsDir); + await fs.mkdirp(appsDir); await templateApp('Arm64Asar.app', 'arm64', async (appPath) => { await fs.copy( path.resolve(asarsDir, 'app.asar'), diff --git a/test/index.spec.ts b/test/index.spec.ts index c1df51a..de0a7cb 100644 --- a/test/index.spec.ts +++ b/test/index.spec.ts @@ -2,16 +2,12 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import { makeUniversalApp } from '../dist/cjs/index'; -import { - appsPath, - appsOutPath, - createTestApp, - templateApp, - VERIFY_APP_TIMEOUT, - verifyApp, -} from './util'; +import { createTestApp, templateApp, VERIFY_APP_TIMEOUT, verifyApp } from './util'; import { createPackage, createPackageWithOptions } from '@electron/asar'; +const appsPath = path.resolve(__dirname, 'fixtures', 'apps'); +const appsOutPath = path.resolve(__dirname, 'fixtures', 'apps', 'out'); + // See `jest.setup.ts` for app fixture setup process describe('makeUniversalApp', () => { afterEach(async () => { @@ -160,110 +156,128 @@ describe('makeUniversalApp', () => { }, VERIFY_APP_TIMEOUT, ); - it('should generate AsarIntegrity for all asars in the application', async () => { - const { testPath } = await createTestApp('app-2'); - const testAsarPath = path.resolve(appsOutPath, 'app-2.asar'); - await createPackage(testPath, testAsarPath); - const arm64AppPath = await templateApp('Arm64-2.app', 'arm64', async (appPath) => { - await fs.copyFile(testAsarPath, path.resolve(appPath, 'Contents', 'Resources', 'app.asar')); - await fs.copyFile( - testAsarPath, - path.resolve(appPath, 'Contents', 'Resources', 'webapp.asar'), - ); - }); - const x64AppPath = await templateApp('X64-2.app', 'x64', async (appPath) => { - await fs.copyFile(testAsarPath, path.resolve(appPath, 'Contents', 'Resources', 'app.asar')); - await fs.copyFile( - testAsarPath, - path.resolve(appPath, 'Contents', 'Resources', 'webbapp.asar'), - ); - }); - const outAppPath = path.resolve(appsOutPath, 'MultipleAsars.app'); + // TODO: Investigate if this should even be allowed. + // Current logic detects all unpacked files as APP_CODE, which doesn't seem correct since it could also be a macho file requiring lipo + // https://github.com/electron/universal/blob/d90d573ccf69a5b14b91aa818c8b97e0e6840399/src/file-utils.ts#L48-L49 + it.skip( + 'should shim asars with different unpacked dirs', + async () => { + const arm64AppPath = await templateApp('UnpackedArm64.app', 'arm64', async (appPath) => { + const { testPath } = await createTestApp('UnpackedAppArm64'); + await createPackageWithOptions( + testPath, + path.resolve(appPath, 'Contents', 'Resources', 'app.asar'), + { + unpackDir: 'var', + unpack: '*.txt', + }, + ); + }); - it( - 'should shim asars with different unpacked dirs', - async () => { - const arm64AppPath = await templateApp('UnpackedArm64.app', 'arm64', async (appPath) => { - const { testPath } = await createTestApp('UnpackedAppArm64'); - await createPackageWithOptions( - testPath, - path.resolve(appPath, 'Contents', 'Resources', 'app.asar'), - { - unpackDir: 'var', - unpack: '*.txt', - }, - ); - }); + const x64AppPath = await templateApp('UnpackedX64.app', 'x64', async (appPath) => { + const { testPath } = await createTestApp('UnpackedAppX64'); + await createPackageWithOptions( + testPath, + path.resolve(appPath, 'Contents', 'Resources', 'app.asar'), + {}, + ); + }); - const x64AppPath = await templateApp('UnpackedX64.app', 'x64', async (appPath) => { - const { testPath } = await createTestApp('UnpackedAppX64'); - await createPackageWithOptions( - testPath, - path.resolve(appPath, 'Contents', 'Resources', 'app.asar'), - {}, - ); - }); + const outAppPath = path.resolve(appsOutPath, 'UnpackedDir.app'); + await makeUniversalApp({ + x64AppPath, + arm64AppPath, + outAppPath, + }); + await verifyApp(outAppPath); + }, + VERIFY_APP_TIMEOUT, + ); - const outAppPath = path.resolve(appsOutPath, 'UnpackedDir.app'); - await makeUniversalApp({ - x64AppPath, - arm64AppPath, - outAppPath, - mergeASARs: true, - }); - await verifyApp(outAppPath); - }, - VERIFY_APP_TIMEOUT, - ); - }); + it( + 'should generate AsarIntegrity for all asars in the application', + async () => { + const { testPath } = await createTestApp('app-2'); + const testAsarPath = path.resolve(appsOutPath, 'app-2.asar'); + await createPackage(testPath, testAsarPath); - describe('no asar mode', () => { - it( - 'should correctly merge two identical app folders', - async () => { - const out = path.resolve(appsOutPath, 'MergedNoAsar.app'); - await makeUniversalApp({ - x64AppPath: path.resolve(appsPath, 'X64NoAsar.app'), - arm64AppPath: path.resolve(appsPath, 'Arm64NoAsar.app'), - outAppPath: out, - }); - await verifyApp(out); - }, - VERIFY_APP_TIMEOUT, - ); + const arm64AppPath = await templateApp('Arm64-2.app', 'arm64', async (appPath) => { + await fs.copyFile( + testAsarPath, + path.resolve(appPath, 'Contents', 'Resources', 'app.asar'), + ); + await fs.copyFile( + testAsarPath, + path.resolve(appPath, 'Contents', 'Resources', 'webapp.asar'), + ); + }); + const x64AppPath = await templateApp('X64-2.app', 'x64', async (appPath) => { + await fs.copyFile( + testAsarPath, + path.resolve(appPath, 'Contents', 'Resources', 'app.asar'), + ); + await fs.copyFile( + testAsarPath, + path.resolve(appPath, 'Contents', 'Resources', 'webbapp.asar'), + ); + }); + const outAppPath = path.resolve(appsOutPath, 'MultipleAsars.app'); + await makeUniversalApp({ + x64AppPath, + arm64AppPath, + outAppPath, + mergeASARs: true, + }); + await verifyApp(outAppPath); + }, + VERIFY_APP_TIMEOUT, + ); + }); - it( - 'should shim two different app folders', - async () => { - const arm64AppPath = await templateApp('ShimArm64.app', 'arm64', async (appPath) => { - const { testPath } = await createTestApp('shimArm64', { - 'i-aint-got-no-rhythm.bin': 'boomshakalaka', - }); - await fs.copy(testPath, path.resolve(appPath, 'Contents', 'Resources', 'app')); - }); + describe('no asar mode', () => { + it( + 'should correctly merge two identical app folders', + async () => { + const out = path.resolve(appsOutPath, 'MergedNoAsar.app'); + await makeUniversalApp({ + x64AppPath: path.resolve(appsPath, 'X64NoAsar.app'), + arm64AppPath: path.resolve(appsPath, 'Arm64NoAsar.app'), + outAppPath: out, + }); + await verifyApp(out); + }, + VERIFY_APP_TIMEOUT, + ); - const x64AppPath = await templateApp('ShimX64.app', 'x64', async (appPath) => { - const { testPath } = await createTestApp('shimX64', { - 'hello-world.bin': 'Hello World', - }); - await fs.copy(testPath, path.resolve(appPath, 'Contents', 'Resources', 'app')); + it( + 'should shim two different app folders', + async () => { + const arm64AppPath = await templateApp('ShimArm64.app', 'arm64', async (appPath) => { + const { testPath } = await createTestApp('shimArm64', { + 'i-aint-got-no-rhythm.bin': 'boomshakalaka', }); + await fs.copy(testPath, path.resolve(appPath, 'Contents', 'Resources', 'app')); + }); - const outAppPath = path.resolve(appsOutPath, 'ShimNoAsar.app'); - await makeUniversalApp({ - x64AppPath, - arm64AppPath, - outAppPath, - }); - await verifyApp(outAppPath); - }, - VERIFY_APP_TIMEOUT, - ); - }); + const x64AppPath = await templateApp('ShimX64.app', 'x64', async (appPath) => { + const { testPath } = await createTestApp('shimX64', { 'hello-world.bin': 'Hello World' }); + await fs.copy(testPath, path.resolve(appPath, 'Contents', 'Resources', 'app')); + }); - // TODO: Add tests for - // * different app dirs with different macho files - // * identical app dirs with universal macho files + const outAppPath = path.resolve(appsOutPath, 'ShimNoAsar.app'); + await makeUniversalApp({ + x64AppPath, + arm64AppPath, + outAppPath, + }); + await verifyApp(outAppPath); + }, + VERIFY_APP_TIMEOUT, + ); }); + + // TODO: Add tests for + // * different app dirs with different macho files + // * identical app dirs with universal macho files }); diff --git a/test/util.ts b/test/util.ts index a7c548a..1937317 100644 --- a/test/util.ts +++ b/test/util.ts @@ -1,4 +1,3 @@ -import { getRawHeader } from '@electron/asar'; import { downloadArtifact } from '@electron/get'; import { spawn } from '@malept/cross-spawn-promise'; import * as zip from 'cross-zip'; @@ -6,6 +5,7 @@ import * as fs from 'fs-extra'; import * as path from 'path'; import plist from 'plist'; import * as fileUtils from '../dist/cjs/file-utils'; +import { getRawHeader } from '@electron/asar'; // We do a LOT of verifications in `verifyApp` 😅 // exec universal binary -> verify ALL asars -> verify ALL app dirs -> verify ALL asar integrity entries @@ -13,8 +13,8 @@ import * as fileUtils from '../dist/cjs/file-utils'; export const VERIFY_APP_TIMEOUT = 80 * 1000; export const asarsDir = path.resolve(__dirname, 'fixtures', 'asars'); -export const appsPath = path.resolve(__dirname, 'fixtures', 'apps'); -export const appsOutPath = path.resolve(appsPath, 'out'); +export const appsDir = path.resolve(__dirname, 'fixtures', 'apps'); +export const appsOutPath = path.resolve(appsDir, 'out'); export const verifyApp = async (appPath: string) => { await ensureUniversal(appPath); @@ -130,7 +130,7 @@ export const createTestApp = async ( additionalFiles: Record = {}, ) => { const outDir = (testName || 'app') + Math.floor(Math.random() * 100); // tests run in parallel, randomize dir suffix to prevent naming collisions - const testPath = path.join(appsPath, outDir); + const testPath = path.join(appsDir, outDir); await fs.remove(testPath); await fs.copy(path.join(asarsDir, 'app'), testPath); @@ -171,9 +171,9 @@ export const templateApp = async ( platform: 'darwin', arch, }); - const appPath = path.resolve(appsPath, name); - zip.unzipSync(electronZip, appsPath); - await fs.rename(path.resolve(appsPath, 'Electron.app'), appPath); + const appPath = path.resolve(appsDir, name); + zip.unzipSync(electronZip, appsDir); + await fs.rename(path.resolve(appsDir, 'Electron.app'), appPath); await fs.remove(path.resolve(appPath, 'Contents', 'Resources', 'default_app.asar')); await modify(appPath);