Skip to content

Commit

Permalink
feat: find project root in mono repositories
Browse files Browse the repository at this point in the history
  • Loading branch information
acostalima committed Jan 28, 2021
1 parent 3c66faf commit 495ff4a
Show file tree
Hide file tree
Showing 11 changed files with 270 additions and 91 deletions.
18 changes: 9 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,15 +44,15 @@ Usage
Default glob: **/test?(s)/**/?(*.)+(spec|test).js.
Options
--plaform, -p Platform on which to run the test suite on. One of: 'ios', 'android'.
--simulator, -s iOS simulator to run the test suite on.
--emulator, -e Android emulator or virtual device (AVD) to run the test suite on.
--metroPort, -p Port on which Metro's server should listen to. [Default: 8081]
--cwd Current directory. [Default: process.cwd()]
--rn React Native version to use. [Default: 0.63.4]
--runner Test runner to use. One of: 'zora', 'mocha'. [Default: 'zora']
--require Path to the module to load before the test suite. If not absolute, cwd is used to resolve the path.
--removeTestApp Removes the test app directory after running the test suite. [Default: false]
--plaform, -p Platform on which to run the test suite on. One of: 'ios', 'android'.
--simulator, -s iOS simulator to run the test suite on.
--emulator, -e Android emulator or virtual device (AVD) to run the test suite on.
--metroPort, -p Port on which Metro's server should listen to. [Default: 8081]
--cwd Current directory. [Default: process.cwd()]
--rn React Native version to use. [Default: 0.63.4]
--runner Test runner to use. One of: 'zora', 'mocha'. [Default: 'zora']
--require Path to the module to load before the test suite. If not absolute, cwd is used to resolve the path.
--removeNativeTestApp Removes the natuve test app directory after running the test suite. [Default: false]
Examples
# Run tests on iPhone 11 simulator with iOS version 14.1 runtime
Expand Down
30 changes: 15 additions & 15 deletions cli/create-test-app.js → cli/create-native-test-app.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,16 @@ const applyPatches = async (cwd, loader, appPath, patches) => {
module.exports = async ({
cwd,
rn: reactNativeVersion = '0.63.4',
testAppPath = path.join(os.homedir(), '.npm', 'rn-test-app'),
nativeTestAppRoot = path.join(os.homedir(), '.npm', 'rn-test-app'),
nativeModules = [],
patches = [],
} = {}) => {
const loader = ora();

const removeTestApp = async () => {
loader.start(`Removing test app at ${testAppPath}`);
const removeNativeTestApp = async () => {
loader.start(`Removing test app at ${nativeTestAppRoot}`);
try {
await rmdir(testAppPath, { recursive: true });
await rmdir(nativeTestAppRoot, { recursive: true });
loader.succeed();
} catch (error) {
loader.fail();
Expand All @@ -65,23 +65,23 @@ module.exports = async ({
let appPkg = null;

try {
appPkg = await readPkg({ cwd: testAppPath });
appPkg = await readPkg({ cwd: nativeTestAppRoot });
} catch (error) {
// Test app not installed
}

if (appPkg && semver.neq(reactNativeVersion, appPkg.dependencies['react-native'])) {
await removeTestApp();
await removeNativeTestApp();
appPkg = null;
}

if (appPkg) {
const modulesToInstall = getNativeModulesToInstall(appPkg, nativeModules);

await installNativeModules(loader, testAppPath, modulesToInstall);
await applyPatches(cwd, loader, testAppPath, patches);
await installNativeModules(loader, nativeTestAppRoot, modulesToInstall);
await applyPatches(cwd, loader, nativeTestAppRoot, patches);

return { testAppPath, removeTestApp };
return { nativeTestAppRoot, removeNativeTestApp };
}

try {
Expand All @@ -94,22 +94,22 @@ module.exports = async ({
await execa('npm', ['install', `react-native@${reactNativeVersion}`], { cwd: reactNativeCliPkgPath });
loader.succeed();

loader.start(`Initializing test app with React Native ${reactNativeVersion} at ${testAppPath}`);
loader.start(`Initializing test app with React Native ${reactNativeVersion} at ${nativeTestAppRoot}`);
await execa('npx', [
'react-native', 'init', 'Test', '--npm', '--version', reactNativeVersion, '--directory', testAppPath, '--title', 'Test',
'react-native', 'init', 'Test', '--npm', '--version', reactNativeVersion, '--directory', nativeTestAppRoot, '--title', 'Test',
], { cwd: reactNativeCliPkgPath });
loader.succeed();

await installNativeModules(loader, testAppPath, nativeModules);
await applyPatches(cwd, loader, testAppPath, patches);
await installNativeModules(loader, nativeTestAppRoot, nativeModules);
await applyPatches(cwd, loader, nativeTestAppRoot, patches);
});
} catch (error) {
loader.fail();

await removeTestApp();
await removeNativeTestApp();

throw error;
}

return { testAppPath, removeTestApp };
return { nativeTestAppRoot, removeNativeTestApp };
};
32 changes: 16 additions & 16 deletions cli/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ const { lilconfigSync: loadConfigFile } = require('lilconfig');
const runIOS = require('./run-ios');
const runAndroid = require('./run-android');
const runMetroServer = require('./metro-server');
const createTestApp = require('./create-test-app');
const createNativeTestApp = require('./create-native-test-app');
const createTestRunner = require('./test-runners');

const fileConfigExplorer = loadConfigFile('rn-test', { stopDir: process.cwd() });
Expand All @@ -22,15 +22,15 @@ Usage
Default glob: **/test?(s)/**/?(*.)+(spec|test).js.
Options
--plaform, -p Platform on which to run the test suite on. One of: 'ios', 'android'.
--simulator, -s iOS simulator to run the test suite on.
--emulator, -e Android emulator or virtual device (AVD) to run the test suite on.
--metroPort, -p Port on which Metro's server should listen to. [Default: 8081]
--cwd Current directory. [Default: process.cwd()]
--rn React Native version to use. [Default: 0.63.4]
--runner Test runner to use. One of: 'zora', 'mocha'. [Default: 'zora']
--require Path to the module to load before the test suite. If not absolute, cwd is used to resolve the path.
--removeTestApp Removes the test app directory after running the test suite. [Default: false]
--plaform, -p Platform on which to run the test suite on. One of: 'ios', 'android'.
--simulator, -s iOS simulator to run the test suite on.
--emulator, -e Android emulator or virtual device (AVD) to run the test suite on.
--metroPort, -p Port on which Metro's server should listen to. [Default: 8081]
--cwd Current directory. [Default: process.cwd()]
--rn React Native version to use. [Default: 0.63.4]
--runner Test runner to use. One of: 'zora', 'mocha'. [Default: 'zora']
--require Path to the module to load before the test suite. If not absolute, cwd is used to resolve the path.
--removeNativeTestApp Removes the natuve test app directory after running the test suite. [Default: false]
Examples
# Run tests on iPhone 11 simulator with iOS version 14.1 runtime
Expand Down Expand Up @@ -122,10 +122,10 @@ if (!semver.valid(options.rn)) {
process.exit(2);
}

const runNativePlatform = (options) => {
const runNativeTestApp = (options) => {
options = {
projectPath: path.join(options.testAppPath, options.platform),
...options,
nativeTestAppRoot: path.join(options.nativeTestAppRoot, options.platform),
};

switch (options.platform) {
Expand All @@ -141,21 +141,21 @@ const runNativePlatform = (options) => {
const runTests = async (options, testFileGlobs) => {
try {
const testRunner = createTestRunner(options, testFileGlobs);
const { testAppPath, removeTestApp } = await createTestApp(options);
const { nativeTestAppRoot, removeNativeTestApp } = await createNativeTestApp(options);

await runMetroServer({
cwd: options.cwd,
port: options.metroPort,
testAppPath,
nativeTestAppRoot,
testRunner,
});

const shutdownNativePlatform = await runNativePlatform({ ...options, testAppPath });
const shutdownNativePlatform = await runNativeTestApp({ ...options, nativeTestAppRoot });

await testRunner.reporter.waitForTests();
await shutdownNativePlatform();

options.removeTestApp && await removeTestApp();
options.removeNativeTestApp && await removeNativeTestApp();

process.exit(testRunner.reporter.didPass() ? 0 : 1);
} catch (error) {
Expand Down
24 changes: 14 additions & 10 deletions cli/metro-server.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ const path = require('path');
const { promisify } = require('util');
const ora = require('ora');
const isCI = require('is-ci');
const { findMonoRepoRoot } = require('./utils');

const INTERNAL_CALLSITES_REGEX = new RegExp(
[
Expand All @@ -25,8 +26,8 @@ const INTERNAL_CALLSITES_REGEX = new RegExp(
].join('|'),
);

const getTestAppDependencies = (testAppPath) => {
const testAppModulesPath = path.join(testAppPath, 'node_modules');
const getTestAppDependencies = (nativeTestAppRoot) => {
const testAppModulesPath = path.join(nativeTestAppRoot, 'node_modules');
const reactNativePath = path.join(testAppModulesPath, 'react-native');

return {
Expand Down Expand Up @@ -70,7 +71,8 @@ const createResolveModule = (testRunner, testAppModulesPath) => (context, module
};

const getMetroConfig = ({
testAppPath,
cwd,
nativeTestAppRoot,
port,
testRunner,
}) => {
Expand All @@ -86,8 +88,9 @@ const getMetroConfig = ({
minifierPath,
assetRegistryPath,
},
} = getTestAppDependencies(testAppPath);
const projectRoot = path.join(__dirname, '..');
} = getTestAppDependencies(nativeTestAppRoot);
const jsAppRoot = path.join(__dirname, '..');
const monoRepoRoot = findMonoRepoRoot(cwd);

const resolveModule = createResolveModule(testRunner, testAppModulesPath);
const testRunnerConfigFilePath = testRunner.writeConfigFile();
Expand Down Expand Up @@ -147,11 +150,12 @@ const getMetroConfig = ({
},
}),
},
projectRoot,
projectRoot: jsAppRoot,
watchFolders: [
process.cwd(),
testAppPath,
projectRoot,
cwd,
monoRepoRoot,
jsAppRoot,
nativeTestAppRoot,
path.dirname(testSuiteFilePath),
path.dirname(testRunnerConfigFilePath),
...testDirectoryPaths,
Expand All @@ -170,7 +174,7 @@ const runServer = async (options) => {
reactNativeModules: {
cliServerApi: { createDevServerMiddleware },
},
} = getTestAppDependencies(options.testAppPath);
} = getTestAppDependencies(options.nativeTestAppRoot);

const defaultConfig = await getDefaultConfig();
const config = mergeConfig(defaultConfig, getMetroConfig(options));
Expand Down
18 changes: 9 additions & 9 deletions cli/run-android.js
Original file line number Diff line number Diff line change
Expand Up @@ -96,15 +96,15 @@ const uninstallApp = async ({ packageName }) => {
}
};

const buildApk = async ({ projectPath, metroPort }) => {
const buildApk = async ({ nativeTestAppRoot, metroPort }) => {
const gradle = process.platform === 'win32' ? 'gradlew.bat' : './gradlew';
const buildProcess = execa(
`${gradle}`,
// -PreactNativeDevServerPort works great!
// https://github.com/facebook/react-native/pull/23616
// https://github.com/react-native-community/cli/pull/421/files
['assembleDebug', `-PreactNativeDevServerPort=${metroPort}`],
{ cwd: projectPath },
{ cwd: nativeTestAppRoot },
);
const loader = ora();

Expand Down Expand Up @@ -261,9 +261,9 @@ const bootHeadlessEmulator = async ({ emulator }) => {
]);
};

const getProjectSettings = ({ projectPath }) => {
const getProjectSettings = ({ nativeTestAppRoot }) => {
const manifestPath = path.join(
projectPath,
nativeTestAppRoot,
'app',
'src',
'main',
Expand All @@ -275,7 +275,7 @@ const getProjectSettings = ({ projectPath }) => {
const [, packageName] = packageNameMatchResult;

const buildDirectory = path.join(
projectPath,
nativeTestAppRoot,
'app',
'build',
'outputs',
Expand All @@ -284,7 +284,7 @@ const getProjectSettings = ({ projectPath }) => {
);

const stringsPath = path.join(
projectPath,
nativeTestAppRoot,
'app',
'src',
'main',
Expand All @@ -308,16 +308,16 @@ const getProjectSettings = ({ projectPath }) => {
};
};

module.exports = async ({ emulator, projectPath, metroPort }) => {
module.exports = async ({ emulator, nativeTestAppRoot, metroPort }) => {
const { apkFilePath, mainActivity, packageName } = getProjectSettings({
projectPath,
nativeTestAppRoot,
});
const {
emulatorId,
shutdown: shutdownEmulator,
} = await bootHeadlessEmulator({ emulator });

await buildApk({ projectPath, metroPort });
await buildApk({ nativeTestAppRoot, metroPort });
await installApp({ emulatorId, apkFilePath });
await launchApp({ mainActivity, packageName });

Expand Down
20 changes: 10 additions & 10 deletions cli/run-ios.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ const findSimulatorByName = (querySimulatorName) => {
};
};

const getProjectSettings = ({ projectPath }) => {
const getProjectSettings = ({ nativeTestAppRoot }) => {
const xcodebuildArgs = [
'-workspace',
'Test.xcworkspace',
Expand All @@ -125,7 +125,7 @@ const getProjectSettings = ({ projectPath }) => {
];

const { stdout } = execa.sync('xcodebuild', xcodebuildArgs, {
cwd: projectPath,
cwd: nativeTestAppRoot,
});
const projectSettings = JSON.parse(stdout);

Expand All @@ -147,7 +147,7 @@ const getProjectSettings = ({ projectPath }) => {
}
};

const buildApp = async ({ projectPath, simulator, metroPort }) => {
const buildApp = async ({ nativeTestAppRoot, simulator, metroPort }) => {
const xcodebuildArgs = [
'-workspace',
'Test.xcworkspace',
Expand All @@ -163,7 +163,7 @@ const buildApp = async ({ projectPath, simulator, metroPort }) => {
const loader = ora();

const buildProcess = execa('xcodebuild', xcodebuildArgs, {
cwd: projectPath,
cwd: nativeTestAppRoot,
env: {
...process.env,
RCT_NO_LAUNCH_PACKAGER: true,
Expand Down Expand Up @@ -296,10 +296,10 @@ const terminateApp = async ({ simulator, bundleId }) => {
}
};

const runPodInstall = async ({ projectPath }) => {
const runPodInstall = async ({ nativeTestAppRoot }) => {
const loader = ora();
const process = execa('pod', ['install'], {
cwd: projectPath,
cwd: nativeTestAppRoot,
});

loader.start('Installing Pods');
Expand All @@ -315,7 +315,7 @@ const runPodInstall = async ({ projectPath }) => {

module.exports = async ({
simulator: inputSimulator,
projectPath,
nativeTestAppRoot,
metroPort,
}) => {
if (isUUID.v4(inputSimulator)) {
Expand All @@ -340,10 +340,10 @@ module.exports = async ({
await bootHeadlessSimulator(simulator);
}

await runPodInstall({ projectPath });
await buildApp({ projectPath, simulator, metroPort });
await runPodInstall({ nativeTestAppRoot });
await buildApp({ nativeTestAppRoot, simulator, metroPort });

const { binaryFilePath, bundleId } = getProjectSettings({ projectPath });
const { binaryFilePath, bundleId } = getProjectSettings({ nativeTestAppRoot });

await installApp({ simulator, binaryFilePath, bundleId });
await launchApp({ simulator, bundleId });
Expand Down
Loading

0 comments on commit 495ff4a

Please sign in to comment.