Fresh history cut from cynarAI/evoo antigravity-handoff/project-timeline (Voice First + project timeline). Brand docs/titles as evOo; keep @rakazo/* packages as tech debt. Excludes node_modules, caches, secrets, voice blobs.
115 lines
4.0 KiB
JavaScript
115 lines
4.0 KiB
JavaScript
import { describe, expect, it } from "vitest";
|
|
import { applySceneLifecycleToAppDelegate, applySceneManifest } from "./with-scene-lifecycle.js";
|
|
|
|
// Matches expo@57.0.23's prebuilt AppDelegate template.
|
|
const TEMPLATE = `internal import Expo
|
|
import React
|
|
import ReactAppDependencyProvider
|
|
|
|
@main
|
|
class AppDelegate: ExpoAppDelegate {
|
|
var window: UIWindow?
|
|
|
|
var reactNativeDelegate: ExpoReactNativeFactoryDelegate?
|
|
var reactNativeFactory: RCTReactNativeFactory?
|
|
|
|
public override func application(
|
|
_ application: UIApplication,
|
|
didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil
|
|
) -> Bool {
|
|
let delegate = ReactNativeDelegate()
|
|
let factory = ExpoReactNativeFactory(delegate: delegate)
|
|
delegate.dependencyProvider = RCTAppDependencyProvider()
|
|
|
|
reactNativeDelegate = delegate
|
|
reactNativeFactory = factory
|
|
|
|
#if os(iOS) || os(tvOS)
|
|
window = UIWindow(frame: UIScreen.main.bounds)
|
|
factory.startReactNative(
|
|
withModuleName: "main",
|
|
in: window,
|
|
launchOptions: launchOptions)
|
|
#endif
|
|
|
|
return super.application(application, didFinishLaunchingWithOptions: launchOptions)
|
|
}
|
|
|
|
// Linking API
|
|
public override func application(
|
|
_ app: UIApplication,
|
|
open url: URL,
|
|
options: [UIApplication.OpenURLOptionsKey: Any] = [:]
|
|
) -> Bool {
|
|
return super.application(app, open: url, options: options) || RCTLinkingManager.application(app, open: url, options: options)
|
|
}
|
|
}
|
|
|
|
class ReactNativeDelegate: ExpoReactNativeFactoryDelegate {
|
|
override func bundleURL() -> URL? {
|
|
nil
|
|
}
|
|
}
|
|
`;
|
|
|
|
describe("applySceneLifecycleToAppDelegate", () => {
|
|
it("hands window creation to Expo's scene delegate", () => {
|
|
const result = applySceneLifecycleToAppDelegate(TEMPLATE);
|
|
|
|
// The app delegate only provides the factory; the scene manifest routes
|
|
// to Expo's EXExpoAppSceneDelegate, so no window code stays behind.
|
|
expect(result).toContain(
|
|
"class AppDelegate: ExpoAppDelegate, ExpoReactNativeFactoryProvider {",
|
|
);
|
|
expect(result).toContain("let factory = ExpoReactNativeFactory(delegate: delegate)");
|
|
expect(result).not.toContain("UIWindow(frame: UIScreen.main.bounds)");
|
|
expect(result).not.toContain("startReactNative(");
|
|
expect(result).not.toContain("class SceneDelegate");
|
|
// Pre-existing template behavior is untouched.
|
|
expect(result).toContain("RCTLinkingManager.application(app, open: url, options: options)");
|
|
expect(result).toContain("class ReactNativeDelegate: ExpoReactNativeFactoryDelegate");
|
|
});
|
|
|
|
it("is idempotent", () => {
|
|
const once = applySceneLifecycleToAppDelegate(TEMPLATE);
|
|
expect(applySceneLifecycleToAppDelegate(once)).toBe(once);
|
|
});
|
|
|
|
it("fails loudly when the Expo template drifts", () => {
|
|
expect(() => applySceneLifecycleToAppDelegate("class AppDelegate: ExpoAppDelegate {}")).toThrow(
|
|
/template changed/,
|
|
);
|
|
});
|
|
|
|
it("fails loudly when only one anchor survives template drift", () => {
|
|
const noLaunchingBlock = TEMPLATE.replace(/#if os\(iOS\)[\s\S]*?#endif\n\n/, "");
|
|
expect(() => applySceneLifecycleToAppDelegate(noLaunchingBlock)).toThrow(/template changed/);
|
|
});
|
|
|
|
it("refuses a partially migrated AppDelegate instead of skipping it", () => {
|
|
const partial = TEMPLATE.replace(
|
|
"class AppDelegate: ExpoAppDelegate {",
|
|
"class AppDelegate: ExpoAppDelegate, ExpoReactNativeFactoryProvider {",
|
|
);
|
|
expect(() => applySceneLifecycleToAppDelegate(partial)).toThrow(/refusing to guess/);
|
|
});
|
|
});
|
|
|
|
describe("applySceneManifest", () => {
|
|
it("declares a single-scene manifest pointing at Expo's scene delegate", () => {
|
|
const infoPlist = applySceneManifest({});
|
|
|
|
expect(infoPlist.UIApplicationSceneManifest).toEqual({
|
|
UIApplicationSupportsMultipleScenes: false,
|
|
UISceneConfigurations: {
|
|
UIWindowSceneSessionRoleApplication: [
|
|
{
|
|
UISceneConfigurationName: "main",
|
|
UISceneDelegateClassName: "EXExpoAppSceneDelegate",
|
|
},
|
|
],
|
|
},
|
|
});
|
|
});
|
|
});
|