import { expect, test } from "bun:test"; import { existsSync, mkdirSync, mkdtempSync, readFileSync, writeFileSync } from "node:fs"; import { tmpdir } from "node:os"; import { join } from "node:path"; import { generateMobile, mobileOptions } from "../src/mobile.ts"; import { runMobileCommand } from "../src/mobile-command.ts"; import { nativeCatalog, runNativeCommand } from "../src/native-command.ts"; test("generateMobile scaffolds a configurable Capacitor project", async () => { const dir = mkdtempSync(join(tmpdir(), "wrn-mobile-")); writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "@acme/store-front" })); await generateMobile(dir, { appId: "com.acme.store", appName: "Acme Store", serverUrl: "https://store.example.com", }); const config = readFileSync(join(dir, "mobile", "capacitor.config.ts"), "utf8"); expect(config).toContain('appId: mobile.appId ?? "com.acme.store"'); expect(config).toContain('appName: mobile.appName ?? "Acme Store"'); expect(config).toContain("...(mobile.capacitor ?? {})"); expect(config).not.toContain("errorPath:"); expect(config).toContain('"https://store.example.com"'); expect(existsSync(join(dir, "mobile", "web", "index.html"))).toBe(true); const pkg = JSON.parse(readFileSync(join(dir, "mobile", "package.json"), "utf8")); expect(pkg.dependencies["@capacitor/android"]).toBe("^8.0.0"); expect(pkg.scripts["open:ios"]).toBe("cap open ios"); }); test("generateMobile derives defaults and does not overwrite files", async () => { const dir = mkdtempSync(join(tmpdir(), "wrn-mobile-")); writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "demo-app" })); await generateMobile(dir); const path = join(dir, "mobile", "capacitor.config.ts"); const before = readFileSync(path, "utf8"); expect(before).toContain('appId: mobile.appId ?? "com.example.demoapp"'); expect(before).toContain('"http://localhost:3000"'); await generateMobile(dir, { appName: "Changed" }); expect(readFileSync(path, "utf8")).toBe(before); }); test("mobileOptions parses generator flags", () => { expect( mobileOptions(["--app-id=com.acme.app", "--app-name=Acme", "--url=https://app.test"]), ).toEqual({ appId: "com.acme.app", appName: "Acme", serverUrl: "https://app.test", mode: undefined, }); }); test("generateMobile scaffolds a WebView-free native project from config", async () => { const dir = mkdtempSync(join(tmpdir(), "wrn-native-")); writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "native-demo" })); writeFileSync( join(dir, "wrnexus.config.mjs"), `export default { mobile: { mode: "native", appId: "com.acme.native", apiUrl: "https://api.example.com" } }`, ); await generateMobile(dir); const pkg = JSON.parse(readFileSync(join(dir, "mobile", "package.json"), "utf8")); expect(pkg.wrnexus.mode).toBe("native"); expect(pkg.dependencies.expo).toBeDefined(); expect(existsSync(join(dir, "mobile", "app", "index.tsx"))).toBe(true); expect(readFileSync(join(dir, "mobile", "src", "wrnexus.ts"), "utf8")).toContain( "https://api.example.com", ); expect(existsSync(join(dir, "mobile", "capacitor.config.ts"))).toBe(false); }); test("mobile compile turns wrn pages into Expo routes", async () => { const dir = mkdtempSync(join(tmpdir(), "wrn-native-compile-")); writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "native-compile" })); writeFileSync(join(dir, "wrnexus.config.mjs"), `export default { mobile: { mode: "native" } }`); await generateMobile(dir); const pages = join(dir, "app", "pages"); const nested = join(pages, "account"); mkdirSync(nested, { recursive: true }); writeFileSync( join(nested, "profile.wrn"), "page Profile { view {

Profile

} }", ); await runMobileCommand(dir, "compile"); const output = readFileSync(join(dir, "mobile", "app", "account", "profile.tsx"), "utf8"); expect(output).toContain("function Profile()"); expect(output).toContain("Profile"); }); test("native catalog uses built-in React Native sharing without installing expo-sharing", async () => { expect(nativeCatalog.share?.expo).toBeUndefined(); const dir = mkdtempSync(join(tmpdir(), "wrn-native-share-")); writeFileSync(join(dir, "package.json"), JSON.stringify({ name: "native-share" })); writeFileSync(join(dir, "wrnexus.config.mjs"), `export default { mobile: { mode: "native" } }`); await generateMobile(dir); await runNativeCommand(dir, "add", ["share"]); const pkg = JSON.parse(readFileSync(join(dir, "mobile", "package.json"), "utf8")); expect(pkg.dependencies["expo-sharing"]).toBeUndefined(); });