Skip to content
Source
Sign inSign up

withAndroidTabletOrientation.test.ts

102 lines4.3 KB

1import * as NodeFS from "node:fs";
2import * as NodeOS from "node:os";
3import * as NodePath from "node:path";
4import { AndroidConfig, compileModsAsync, XML } from "expo/config-plugins";
5import { afterEach, describe, expect, it } from "vite-plus/test";
6​
7import withAndroidTabletOrientation from "./withAndroidTabletOrientation.cjs";
8​
9const MANIFEST = `<manifest xmlns:android="http://schemas.android.com/apk/res/android">
10 <application android:name=".MainApplication">
11 <activity android:name=".MainActivity" android:configChanges="keyboard|keyboardHidden|orientation|screenSize|screenLayout|uiMode|smallestScreenSize" android:exported="true">
12 <intent-filter>
13 <action android:name="android.intent.action.MAIN"/>
14 <category android:name="android.intent.category.LAUNCHER"/>
15 </intent-filter>
16 </activity>
17 </application>
18</manifest>`;
19​
20// Trimmed from the MainActivity.kt that `expo prebuild` generates for this app.
21const MAIN_ACTIVITY = `package com.t3tools.t3code
22​
23import android.os.Build
24import android.os.Bundle
25​
26import com.facebook.react.ReactActivity
27​
28class MainActivity : ReactActivity() {
29 override fun onCreate(savedInstanceState: Bundle?) {
30 // setTheme(R.style.AppTheme);
31 super.onCreate(null)
32 }
33​
34 override fun getMainComponentName(): String = "main"
36`;
37​
38const projectRoots: string[] = [];
39​
40afterEach(() => {
41 for (const root of projectRoots.splice(0)) NodeFS.rmSync(root, { recursive: true, force: true });
42});
43​
44async function prebuildAndroid(input = { manifest: MANIFEST, mainActivity: MAIN_ACTIVITY }) {
45 const projectRoot = NodeFS.mkdtempSync(NodePath.join(NodeOS.tmpdir(), "tablet-orientation-"));
46 projectRoots.push(projectRoot);
47 const sourceRoot = NodePath.join(projectRoot, "android/app/src/main");
48 const activityPath = NodePath.join(sourceRoot, "java/com/t3tools/t3code/MainActivity.kt");
49 const manifestPath = NodePath.join(sourceRoot, "AndroidManifest.xml");
50 NodeFS.mkdirSync(NodePath.dirname(activityPath), { recursive: true });
51 NodeFS.writeFileSync(manifestPath, input.manifest);
52 NodeFS.writeFileSync(activityPath, input.mainActivity);
53​
54 const config = withAndroidTabletOrientation({
55 name: "T3 Code",
56 slug: "t3code",
57 orientation: "portrait",
58 });
59 // Prebuild registers Expo's orientation mod after app plugins. Mods run in
60 // reverse order, so our plugin must remove the portrait lock Expo writes.
61 await compileModsAsync(AndroidConfig.Orientation.withOrientation(config), {
62 projectRoot,
63 platforms: ["android"],
64 });
65 return {
66 manifest: NodeFS.readFileSync(manifestPath, "utf8"),
67 mainActivity: NodeFS.readFileSync(activityPath, "utf8"),
68 };
70​
71describe("withAndroidTabletOrientation", () => {
72 it("generates an unlocked launcher manifest while preserving rotation and fold handling", async () => {
73 const { manifest } = await prebuildAndroid();
74 const mainActivity = AndroidConfig.Manifest.getMainActivityOrThrow(
75 (await XML.parseXMLAsync(manifest)) as AndroidConfig.Manifest.AndroidManifest,
76 );
77​
78 expect(mainActivity.$["android:screenOrientation"]).toBeUndefined();
79 expect(mainActivity.$["android:configChanges"]?.split("|")).toEqual(
80 expect.arrayContaining(["orientation", "screenSize", "smallestScreenSize"]),
81 );
82 });
83​
84 it("generates the phone/tablet policy before activity creation and stays stable on repeated prebuild", async () => {
85 const once = await prebuildAndroid();
86​
87 expect(once.mainActivity).toMatch(
88 /override fun onCreate\(savedInstanceState: Bundle\?\)\s*\{[^}]*applyTabletOrientation\(\)[^}]*super\.onCreate\(null\)/,
89 );
90 expect(once.mainActivity).toMatch(
91 /private fun applyTabletOrientation\(\)\s*\{\s*requestedOrientation = if \(resources\.configuration\.smallestScreenWidthDp >= 600\)\s*\{\s*ActivityInfo\.SCREEN_ORIENTATION_FULL_USER\s*\} else \{\s*ActivityInfo\.SCREEN_ORIENTATION_PORTRAIT\s*\}/,
92 );
93 expect(await prebuildAndroid(once)).toEqual(once);
94 const existingActivity = once.mainActivity
95 .replace(
96 "override fun onCreate(savedInstanceState: Bundle?) {\n applyTabletOrientation()",
97 "override fun onCreate(savedInstanceState: Bundle?) {",
98 )
99 .replace("super.onCreate(null)", "super.onCreate(null)\n applyTabletOrientation()");
100 expect(await prebuildAndroid({ ...once, mainActivity: existingActivity })).toEqual(once);
101 });
102});