1/* eslint-env jest */ 2import JsonFile from '@expo/json-file'; 3import execa 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/ansi-styles/index.js', 33 '../node_modules/arg/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 Description 49 Install a module or other package to a project 50 51 Usage 52 $ npx expo install [packages...] [options] 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 -h, --help Output usage information 60 61 Additional options can be passed to the underlying install command by using -- 62 $ expo install react -- --verbose 63 > yarn add react --verbose 64 " 65 `); 66}); 67 68it( 69 'runs `npx expo install expo-sms`', 70 async () => { 71 const projectRoot = await setupTestProjectAsync('basic-install', 'with-blank'); 72 // `npx expo install expo-sms` 73 await execa('node', [bin, 'install', 'expo-sms'], { cwd: projectRoot }); 74 75 // List output files with sizes for snapshotting. 76 // This is to make sure that any changes to the output are intentional. 77 // Posix path formatting is used to make paths the same across OSes. 78 const files = klawSync(projectRoot) 79 .map((entry) => { 80 if (entry.path.includes('node_modules') || !entry.stats.isFile()) { 81 return null; 82 } 83 return path.posix.relative(projectRoot, entry.path); 84 }) 85 .filter(Boolean); 86 87 const pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 88 89 // Added expected package 90 expect(pkg.dependencies['expo-sms']).toBe('~10.1.0'); 91 expect(pkg.devDependencies).toEqual({ 92 '@babel/core': '^7.12.9', 93 }); 94 95 // Added new packages 96 expect(Object.keys(pkg.dependencies).sort()).toStrictEqual([ 97 'expo', 98 'expo-sms', 99 'react', 100 'react-native', 101 ]); 102 103 expect(files).toStrictEqual(['App.js', 'app.json', 'package.json', 'yarn.lock']); 104 }, 105 // Could take 45s depending on how fast npm installs 106 60 * 1000 107); 108 109it( 110 'runs `npx expo install --check` fails', 111 async () => { 112 const projectRoot = await setupTestProjectAsync('install-check-fail', 'with-blank'); 113 await installAsync(projectRoot, ['add', '[email protected]', '[email protected]']); 114 115 let pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 116 // Added expected package 117 expect(pkg.dependencies['expo-sms']).toBe('1.0.0'); 118 119 try { 120 await execa('node', [bin, 'install', '--check'], { cwd: projectRoot }); 121 throw new Error('SHOULD NOT HAPPEN'); 122 } catch (error) { 123 expect(error.stderr).toMatch(/expo-auth-session@1\.0\.0 - expected version: ~3\.5\.0/); 124 expect(error.stderr).toMatch(/expo-sms@1\.0\.0 - expected version: ~10\.1\.0/); 125 expect(error.stderr).toMatch( 126 /npx expo install expo-auth-session@~3\.5\.0 expo-sms@~10\.1\.0/ 127 ); 128 } 129 130 await expect( 131 execa('node', [bin, 'install', 'expo-sms', '--check'], { cwd: projectRoot }) 132 ).rejects.toThrowError(/expo-sms@1\.0\.0 - expected version: ~10\.1\.0/); 133 134 // Check doesn't fix packages 135 pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 136 // Added expected package 137 expect(pkg.dependencies['expo-sms']).toBe('1.0.0'); 138 }, 139 // Could take 45s depending on how fast npm installs 140 60 * 1000 141); 142 143it( 144 'runs `npx expo install --fix` fails', 145 async () => { 146 const projectRoot = await setupTestProjectAsync('install-fix-fail', 'with-blank'); 147 await installAsync(projectRoot, ['add', '[email protected]', '[email protected]']); 148 149 await execa('node', [bin, 'install', '--fix', 'expo-sms'], { cwd: projectRoot }); 150 151 // Ensure the versions are invalid 152 await expect( 153 execa('node', [bin, 'install', '--check'], { cwd: projectRoot }) 154 ).rejects.toThrow(); 155 156 // Check doesn't fix packages 157 let pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 158 // Added expected package 159 expect(pkg.dependencies['expo-sms']).toBe('~10.1.0'); 160 161 // Didn't fix expo-auth-session since we didn't pass it in 162 expect(pkg.dependencies['expo-auth-session']).toBe('1.0.0'); 163 164 // Fix all versions 165 await execa('node', [bin, 'install', '--fix'], { cwd: projectRoot }); 166 167 // Check that the versions are fixed 168 pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 169 170 // Didn't fix expo-auth-session since we didn't pass it in 171 expect(pkg.dependencies['expo-auth-session']).toBe('~3.5.0'); 172 }, 173 // Could take 45s depending on how fast npm installs 174 60 * 1000 175); 176