import { createRequire } from "node:module"; import { afterEach, describe, expect, it } from "vitest"; // Load the copies the Expo CLI uses for prebuild, not older hoisted ones. const requireFromExpo = createRequire(require.resolve("expo/package.json")); const requireFromExpoCli = createRequire(requireFromExpo.resolve("@expo/cli/package.json")); const { getPrebuildConfigAsync } = requireFromExpoCli("@expo/prebuild-config"); const { compileModsAsync } = requireFromExpoCli( "@expo/config-plugins/build/plugins/mod-compiler.js", ); interface ManifestEntry { $: Record; } const originalFdroidFlag = process.env.PASEO_FDROID_BUILD; async function resolveAndroidManifest() { const { exp } = await getPrebuildConfigAsync(__dirname, { platforms: ["android"] }); await compileModsAsync(exp, { projectRoot: __dirname, introspect: true, platforms: ["android"], assertMissingModProviders: false, }); return exp._internal.modResults.android.manifest.manifest; } describe("Android app config", () => { afterEach(() => { if (originalFdroidFlag === undefined) { delete process.env.PASEO_FDROID_BUILD; } else { process.env.PASEO_FDROID_BUILD = originalFdroidFlag; } }); it.each([ ["Google Play", "0"], ["F-Droid", "1"], ])( "%s build installs on devices without a camera", async (_build, fdroidFlag) => { process.env.PASEO_FDROID_BUILD = fdroidFlag; const manifest = await resolveAndroidManifest(); const permissions = (manifest["uses-permission"] ?? []).map( (entry: ManifestEntry) => entry.$["android:name"], ); const features = (manifest["uses-feature"] ?? []).map((entry: ManifestEntry) => entry.$); expect(permissions).toContain("android.permission.CAMERA"); expect(features).toEqual( expect.arrayContaining([ { "android:name": "android.hardware.camera", "android:required": "false" }, { "android:name": "android.hardware.camera.autofocus", "android:required": "false" }, ]), ); }, 30_000, ); }); it("follows system rotation on Android", async () => { const manifest = await resolveAndroidManifest(); const activity = manifest.application[0].activity.find( (entry: ManifestEntry) => entry.$["android:name"] === ".MainActivity", ); expect(activity.$["android:screenOrientation"]).toBe("unspecified"); }, 30_000); it("allows all orientations on iPhone and iPad", async () => { const { mkdtemp, rm } = await import("node:fs/promises"); const { tmpdir } = await import("node:os"); const { join } = await import("node:path"); const root = await mkdtemp(join(tmpdir(), "paseo-ios-config-")); try { const { exp } = await getPrebuildConfigAsync(__dirname, { platforms: ["ios"] }); await compileModsAsync(exp, { projectRoot: root, introspect: true, platforms: ["ios"], assertMissingModProviders: false, }); const plist = exp._internal.modResults.ios.infoPlist; const orientations = [ "UIInterfaceOrientationPortrait", "UIInterfaceOrientationPortraitUpsideDown", "UIInterfaceOrientationLandscapeLeft", "UIInterfaceOrientationLandscapeRight", ]; expect(plist.UISupportedInterfaceOrientations).toEqual(orientations); expect(plist["UISupportedInterfaceOrientations~ipad"]).toEqual(orientations); } finally { await rm(root, { recursive: true, force: true }); } }, 30_000);