Skip to content
Draft
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
{
"expo": {
"name": "react-native-expo-52",
"slug": "react-native-expo-52",
"name": "react-native-expo-53",
"slug": "react-native-expo-53",
"version": "1.0.0",
"orientation": "portrait",
"icon": "./assets/images/icon.png",
"scheme": "myapp",
"userInterfaceStyle": "automatic",
"newArchEnabled": true,
"entryPoint": "./node_modules/expo-router/entry",
"platforms": ["ios", "android", "web"],
"ios": {
"supportsTablet": true
},
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"name": "react-native-expo-52",
"name": "react-native-expo-53",
"main": "expo-router/entry",
"version": "1.0.0",
"scripts": {
Expand All @@ -18,7 +18,7 @@
"@expo/vector-icons": "^14.0.2",
"@react-navigation/bottom-tabs": "^7.2.0",
"@react-navigation/native": "^7.0.14",
"expo": "~52.0.39",
"expo": "^53.0.8",
"expo-blur": "~14.0.3",
"expo-constants": "~17.0.8",
"expo-font": "~13.0.4",
Expand Down
22 changes: 19 additions & 3 deletions e2e-tests/tests/expo.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { Integration } from '../../lib/Constants';
import {
KEYS,
TEST_ARGS,
checkFileContents,
checkFileExists,
checkIfExpoBundles,
cleanupGit,
revertLocalChanges,
startWizardInstance,
} from '../utils';
import { startWizardInstance } from '../utils';
import { checkFileContents } from '../utils';
import { afterAll, beforeAll, describe, test } from 'vitest';
import { afterAll, beforeAll, describe, test, expect } from 'vitest';

describe('Expo', () => {
const integration = Integration.reactNative;
Expand Down Expand Up @@ -116,4 +117,19 @@ module.exports = config;`,
test('.gitignore is updated correctly', () => {
checkFileContents(`${projectDir}/.gitignore`, `.env.local`);
});

test('android project is bundled correctly', async () => {
const bundled = await checkIfExpoBundles(projectDir, 'android');
expect(bundled).toBe(true);
});

test('ios project is bundled correctly', async () => {
const bundled = await checkIfExpoBundles(projectDir, 'ios');
expect(bundled).toBe(true);
});

test('web project is bundled correctly', async () => {
const bundled = await checkIfExpoBundles(projectDir, 'web');
expect(bundled).toBe(true);
});
});
34 changes: 28 additions & 6 deletions e2e-tests/tests/react-native.test.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,16 @@
import * as path from 'node:path';
import { Integration } from '../../lib/Constants';
import { KEYS, TEST_ARGS, cleanupGit, revertLocalChanges } from '../utils';
import { startWizardInstance } from '../utils';
import { checkFileContents } from '../utils';
import { afterAll, beforeAll, describe, test } from 'vitest';
import {
KEYS,
TEST_ARGS,
cleanupGit,
checkFileContents,
checkIfReactNativeBundles,
checkIfReactNativeReleaseBuilds,
revertLocalChanges,
startWizardInstance
} from '../utils';
import { afterAll, beforeAll, describe, test, expect } from 'vitest';

describe('ReactNative', () => {
const integration = Integration.reactNative;
Expand Down Expand Up @@ -45,7 +52,7 @@ describe('ReactNative', () => {
optional: true,
timeout: 5000,
},
));
));

const prettierPrompted =
podInstallPrompted &&
Expand All @@ -62,7 +69,7 @@ describe('ReactNative', () => {
[KEYS.DOWN, KEYS.ENTER],
'Have you successfully sent a test event?',
));

testEventPrompted &&
(await wizardInstance.sendStdinAndWaitForOutput(
// Respond that test event was sent
Expand Down Expand Up @@ -168,4 +175,19 @@ defaults.url=https://sentry.io/`,
`../node_modules/@sentry/react-native/scripts/sentry-xcode-debug-files.sh`,
);
});

test('android project is bundled correctly', async () => {
const bundled = await checkIfReactNativeBundles(projectDir, 'android');
expect(bundled).toBe(true);
});

test('ios project is bundled correctly', async () => {
const bundled = await checkIfReactNativeBundles(projectDir, 'ios');
expect(bundled).toBe(true);
});

test('android project builds correctly', async () => {
const builds = await checkIfReactNativeReleaseBuilds(projectDir, 'android', true);
expect(builds).toBe(true);
});
});
138 changes: 137 additions & 1 deletion e2e-tests/utils/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,9 +68,14 @@ export class WizardTestEnv {
opts?: {
cwd?: string;
debug?: boolean;
env?: NodeJS.ProcessEnv;
},
) {
this.taskHandle = spawn(cmd, args, { cwd: opts?.cwd, stdio: 'pipe' });
if (opts?.env) {
this.taskHandle = spawn(cmd, args, { cwd: opts?.cwd, stdio: 'pipe', env: { ...process.env, ...opts?.env } });
} else {
this.taskHandle = spawn(cmd, args, { cwd: opts?.cwd, stdio: 'pipe' });
}

if (opts?.debug) {
this.taskHandle.stdout?.pipe(process.stdout);
Expand Down Expand Up @@ -472,6 +477,137 @@ export async function checkIfFlutterBuilds(
expect(outputReceived).toBe(true);
}

/**
* Check if the React Native project bundles successfully for the specified platform.
* Returns a boolean indicating if the process exits with status code 0.
* @param projectDir The root directory of the React Native project.
* @param platform The platform to bundle for ('ios' or 'android').
* @param debug runs the command in debug mode if true
*/
export async function checkIfReactNativeBundles(
projectDir: string,
platform: 'ios' | 'android',
debug = false,
): Promise<boolean> {
const entryFile = 'index.js';
const dev = 'false'; // Test a production-like bundle
let bundleOutput: string;
let assetsDest: string;

if (platform === 'ios') {
bundleOutput = './ios/main.jsbundle';
assetsDest = './ios';
} else {
// android
bundleOutput = './android/app/src/main/assets/index.android.bundle';
assetsDest = './android/app/src/main/res';
}

const bundleCommandArgs = [
'react-native',
'bundle',
'--entry-file',
entryFile,
'--platform',
platform,
'--dev',
dev,
'--bundle-output',
bundleOutput,
'--assets-dest',
assetsDest,
];

const testEnv = new WizardTestEnv('npx', bundleCommandArgs, {
cwd: projectDir,
debug: debug,
});

const builtSuccessfully = await testEnv.waitForStatusCode(0, {
timeout: 300_000,
});

testEnv.kill();

return builtSuccessfully;
}

/**
* Check if the React Native project creates a release build locally for the specified platform.
* Returns a boolean indicating if the process exits with status code 0.
* @param projectDir The root directory of the React Native project.
* @param platform The platform to build for ('ios' or 'android').
* @param debug runs the command in debug mode if true
*/
export async function checkIfReactNativeReleaseBuilds(
projectDir: string,
platform: 'ios' | 'android',
debug = false,
): Promise<boolean> {
let command: string;
let args: string[];
let cwd: string;
let env: NodeJS.ProcessEnv;

if (platform === 'android') {
command = './gradlew';
args = ['assembleRelease'];
cwd = path.join(projectDir, 'android');
env = { SENTRY_DISABLE_AUTO_UPLOAD: 'true' };
} else {
// ios
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

iOS will be handled separately since it needs to run only on MacOS.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Followed up in #995

command = 'TODO';
args = ['TODO'];
cwd = path.join(projectDir, 'ios');
env = {};
}

const testEnv = new WizardTestEnv(command, args, {
cwd: cwd,
debug: debug,
env: env,
});

const builtSuccessfully = await testEnv.waitForStatusCode(0, {
timeout: 1_200_000,
});

testEnv.kill();
return builtSuccessfully;
}

/**
* Check if the Expo project exports successfully for the specified platform.
* Returns a boolean indicating if the process exits with status code 0.
* @param projectDir The root directory of the Expo project.
* @param platform The platform to export for ('ios', 'android', or 'web').
* @param debug runs the command in debug mode if true
*/
export async function checkIfExpoBundles(
projectDir: string,
platform: 'ios' | 'android' | 'web',
debug = false,
): Promise<boolean> {
const exportCommandArgs = [
'expo',
'export',
'--platform',
platform,
];

const testEnv = new WizardTestEnv('npx', exportCommandArgs, {
cwd: projectDir,
debug: debug,
});

const builtSuccessfully = await testEnv.waitForStatusCode(0, {
timeout: 300_000,
});

testEnv.kill();
return builtSuccessfully;
}

/**
* Check if the project runs on dev mode
* @param projectDir
Expand Down
Loading