1import spawnAsync from '@expo/spawn-async';
2import chalk from 'chalk';
3
4import { filterAsync } from '../Utils';
5import * as Directories from '../Directories';
6import * as Packages from '../Packages';
7
8const ANDROID_DIR = Directories.getAndroidDir();
9
10const excludedInTests = [
11  'expo-module-template',
12  'expo-notifications',
13  'expo-in-app-purchases',
14  'expo-splash-screen',
15  'unimodules-test-core',
16];
17
18type TestType = 'local' | 'instrumented';
19
20export async function androidNativeUnitTests({ type }: { type: TestType }) {
21  if (!type) {
22    throw new Error(
23      'Must specify which type of unit test to run with `--type local` or `--type instrumented`.'
24    );
25  }
26  if (type !== 'local' && type !== 'instrumented') {
27    throw new Error('Invalid type specified. Must use `--type local` or `--type instrumented`.');
28  }
29
30  const packages = await Packages.getListOfPackagesAsync();
31
32  function consoleErrorOutput(
33    output: string,
34    label: string,
35    colorifyLine: (string) => string
36  ): void {
37    const lines = output.trim().split(/\r\n?|\n/g);
38    console.error(lines.map((line) => `${chalk.gray(label)} ${colorifyLine(line)}`).join('\n'));
39  }
40
41  const androidPackages = await filterAsync(packages, async (pkg) => {
42    const pkgSlug = pkg.packageSlug;
43
44    if (type === 'instrumented') {
45      return (
46        pkg.isSupportedOnPlatform('android') &&
47        (await pkg.hasNativeInstrumentationTestsAsync('android')) &&
48        !excludedInTests.includes(pkgSlug)
49      );
50    } else {
51      return (
52        pkg.isSupportedOnPlatform('android') &&
53        (await pkg.hasNativeTestsAsync('android')) &&
54        !excludedInTests.includes(pkgSlug)
55      );
56    }
57  });
58
59  console.log(chalk.green('Packages to test: '));
60  androidPackages.forEach((pkg) => {
61    console.log(chalk.yellow(pkg.packageSlug));
62  });
63
64  const testCommand = type === 'instrumented' ? 'connectedAndroidTest' : 'test';
65  try {
66    await spawnAsync(
67      './gradlew',
68      androidPackages.map((pkg) => `:${pkg.packageSlug}:${testCommand}`),
69      {
70        cwd: ANDROID_DIR,
71        stdio: 'inherit',
72        env: { ...process.env },
73      }
74    );
75  } catch (error) {
76    console.error('Failed while executing android unit tests');
77    consoleErrorOutput(error.stdout, 'stdout >', chalk.reset);
78    consoleErrorOutput(error.stderr, 'stderr >', chalk.red);
79    throw error;
80  }
81  console.log(chalk.green('Finished android unit tests successfully.'));
82  return;
83}
84
85export default (program: any) => {
86  program
87    .command('android-native-unit-tests')
88    .option('-t, --type <string>', 'Type of unit test to run: local or instrumented')
89    .description('Runs Android native unit tests for each package that provides them.')
90    .asyncAction(androidNativeUnitTests);
91};
92