1/* eslint-env jest */ 2import JsonFile from '@expo/json-file'; 3import execa, { ExecaError } from 'execa'; 4import fs from 'fs/promises'; 5import klawSync from 'klaw-sync'; 6import path from 'path'; 7 8import { 9 execute, 10 projectRoot, 11 getLoadedModulesAsync, 12 bin, 13 setupTestProjectAsync, 14 installAsync, 15} from './utils'; 16 17const originalForceColor = process.env.FORCE_COLOR; 18const originalCI = process.env.CI; 19beforeAll(async () => { 20 await fs.mkdir(projectRoot, { recursive: true }); 21 process.env.FORCE_COLOR = '0'; 22 process.env.CI = '1'; 23}); 24afterAll(() => { 25 process.env.FORCE_COLOR = originalForceColor; 26 process.env.CI = originalCI; 27}); 28 29it('loads expected modules by default', async () => { 30 const modules = await getLoadedModulesAsync(`require('../../build/src/install').expoInstall`); 31 expect(modules).toStrictEqual([ 32 '../node_modules/arg/index.js', 33 '../node_modules/chalk/node_modules/ansi-styles/index.js', 34 '../node_modules/chalk/source/index.js', 35 '../node_modules/chalk/source/util.js', 36 '../node_modules/has-flag/index.js', 37 '../node_modules/supports-color/index.js', 38 '@expo/cli/build/src/install/index.js', 39 '@expo/cli/build/src/log.js', 40 '@expo/cli/build/src/utils/args.js', 41 ]); 42}); 43 44it('runs `npx install install --help`', async () => { 45 const results = await execute('install', '--help'); 46 expect(results.stdout).toMatchInlineSnapshot(` 47 " 48 Info 49 Install a module or other package to a project 50 51 Usage 52 $ npx expo install 53 54 Options 55 --check Check which installed packages need to be updated 56 --fix Automatically update any invalid package versions 57 --npm Use npm to install dependencies. Default when package-lock.json exists 58 --yarn Use Yarn to install dependencies. Default when yarn.lock exists 59 --pnpm Use pnpm to install dependencies. Default when pnpm-lock.yaml exists 60 -h, --help Usage info 61 62 Additional options can be passed to the underlying install command by using -- 63 $ npx expo install react -- --verbose 64 > yarn add react --verbose 65 " 66 `); 67}); 68 69it( 70 'runs `npx expo install expo-sms`', 71 async () => { 72 const projectRoot = await setupTestProjectAsync('basic-install', 'with-blank'); 73 // `npx expo install expo-sms` 74 await execa('node', [bin, 'install', 'expo-sms'], { cwd: projectRoot }); 75 76 // List output files with sizes for snapshotting. 77 // This is to make sure that any changes to the output are intentional. 78 // Posix path formatting is used to make paths the same across OSes. 79 const files = klawSync(projectRoot) 80 .map((entry) => { 81 if (entry.path.includes('node_modules') || !entry.stats.isFile()) { 82 return null; 83 } 84 return path.posix.relative(projectRoot, entry.path); 85 }) 86 .filter(Boolean); 87 88 const pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 89 90 // Added expected package 91 const pkgDependencies = pkg.dependencies as Record<string, string>; 92 expect(pkgDependencies['expo-sms']).toBe('~10.2.0'); 93 expect(pkg.devDependencies).toEqual({ 94 '@babel/core': '^7.12.9', 95 }); 96 97 // Added new packages 98 expect(Object.keys(pkg.dependencies ?? {}).sort()).toStrictEqual([ 99 'expo', 100 'expo-sms', 101 'react', 102 'react-native', 103 ]); 104 105 expect(files).toStrictEqual(['App.js', 'app.json', 'package.json', 'yarn.lock']); 106 }, 107 // Could take 45s depending on how fast npm installs 108 60 * 1000 109); 110 111it( 112 'runs `npx expo install --check` fails', 113 async () => { 114 const projectRoot = await setupTestProjectAsync('install-check-fail', 'with-blank'); 115 await installAsync(projectRoot, ['add', '[email protected]', '[email protected]']); 116 117 let pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 118 // Added expected package 119 let pkgDependencies = pkg.dependencies as Record<string, string>; 120 expect(pkgDependencies['expo-sms']).toBe('1.0.0'); 121 122 try { 123 await execa('node', [bin, 'install', '--check'], { cwd: projectRoot }); 124 throw new Error('SHOULD NOT HAPPEN'); 125 } catch (e) { 126 const error = e as ExecaError; 127 expect(error.stderr).toMatch(/expo-auth-session@1\.0\.0 - expected version: ~3\.\d\.\d/); 128 expect(error.stderr).toMatch(/expo-sms@1\.0\.0 - expected version: ~10\.\d\.\d/); 129 expect(error.stderr).toMatch( 130 /npx expo install expo-auth-session@~3\.\d\.\d expo-sms@~10\.\d\.\d/ 131 ); 132 } 133 134 await expect( 135 execa('node', [bin, 'install', 'expo-sms', '--check'], { cwd: projectRoot }) 136 ).rejects.toThrowError(/expo-sms@1\.0\.0 - expected version: ~10\.\d\.\d/); 137 138 // Check doesn't fix packages 139 pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 140 // Added expected package 141 pkgDependencies = pkg.dependencies as Record<string, string>; 142 expect(pkgDependencies['expo-sms']).toBe('1.0.0'); 143 }, 144 // Could take 45s depending on how fast npm installs 145 60 * 1000 146); 147 148it( 149 'runs `npx expo install --fix` fails', 150 async () => { 151 const projectRoot = await setupTestProjectAsync('install-fix-fail', 'with-blank'); 152 await installAsync(projectRoot, ['add', '[email protected]', '[email protected]']); 153 154 await execa('node', [bin, 'install', '--fix', 'expo-sms'], { cwd: projectRoot }); 155 156 // Ensure the versions are invalid 157 await expect( 158 execa('node', [bin, 'install', '--check'], { cwd: projectRoot }) 159 ).rejects.toThrow(); 160 161 // Check doesn't fix packages 162 let pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 163 // Added expected package 164 let pkgDependencies = pkg.dependencies as Record<string, string>; 165 expect(pkgDependencies['expo-sms']).toBe('~10.2.0'); 166 167 // Didn't fix expo-auth-session since we didn't pass it in 168 expect(pkgDependencies['expo-auth-session']).toBe('1.0.0'); 169 170 // Fix all versions 171 await execa('node', [bin, 'install', '--fix'], { cwd: projectRoot }); 172 173 // Check that the versions are fixed 174 pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 175 176 // Didn't fix expo-auth-session since we didn't pass it in 177 pkgDependencies = pkg.dependencies as Record<string, string>; 178 expect(pkgDependencies['expo-auth-session']).toBe('~3.6.1'); 179 }, 180 // Could take 45s depending on how fast npm installs 181 60 * 1000 182); 183