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 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 expect(pkg.dependencies['expo-sms']).toBe('~10.2.0'); 92 expect(pkg.devDependencies).toEqual({ 93 '@babel/core': '^7.12.9', 94 }); 95 96 // Added new packages 97 expect(Object.keys(pkg.dependencies).sort()).toStrictEqual([ 98 'expo', 99 'expo-sms', 100 'react', 101 'react-native', 102 ]); 103 104 expect(files).toStrictEqual(['App.js', 'app.json', 'package.json', 'yarn.lock']); 105 }, 106 // Could take 45s depending on how fast npm installs 107 60 * 1000 108); 109 110it( 111 'runs `npx expo install --check` fails', 112 async () => { 113 const projectRoot = await setupTestProjectAsync('install-check-fail', 'with-blank'); 114 await installAsync(projectRoot, ['add', '[email protected]', '[email protected]']); 115 116 let pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 117 // Added expected package 118 expect(pkg.dependencies['expo-sms']).toBe('1.0.0'); 119 120 try { 121 await execa('node', [bin, 'install', '--check'], { cwd: projectRoot }); 122 throw new Error('SHOULD NOT HAPPEN'); 123 } catch (error) { 124 expect(error.stderr).toMatch(/expo-auth-session@1\.0\.0 - expected version: ~3\.\d\.\d/); 125 expect(error.stderr).toMatch(/expo-sms@1\.0\.0 - expected version: ~10\.\d\.\d/); 126 expect(error.stderr).toMatch( 127 /npx expo install expo-auth-session@~3\.\d\.\d expo-sms@~10\.\d\.\d/ 128 ); 129 } 130 131 await expect( 132 execa('node', [bin, 'install', 'expo-sms', '--check'], { cwd: projectRoot }) 133 ).rejects.toThrowError(/expo-sms@1\.0\.0 - expected version: ~10\.\d\.\d/); 134 135 // Check doesn't fix packages 136 pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 137 // Added expected package 138 expect(pkg.dependencies['expo-sms']).toBe('1.0.0'); 139 }, 140 // Could take 45s depending on how fast npm installs 141 60 * 1000 142); 143 144it( 145 'runs `npx expo install --fix` fails', 146 async () => { 147 const projectRoot = await setupTestProjectAsync('install-fix-fail', 'with-blank'); 148 await installAsync(projectRoot, ['add', '[email protected]', '[email protected]']); 149 150 await execa('node', [bin, 'install', '--fix', 'expo-sms'], { cwd: projectRoot }); 151 152 // Ensure the versions are invalid 153 await expect( 154 execa('node', [bin, 'install', '--check'], { cwd: projectRoot }) 155 ).rejects.toThrow(); 156 157 // Check doesn't fix packages 158 let pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 159 // Added expected package 160 expect(pkg.dependencies['expo-sms']).toBe('~10.2.0'); 161 162 // Didn't fix expo-auth-session since we didn't pass it in 163 expect(pkg.dependencies['expo-auth-session']).toBe('1.0.0'); 164 165 // Fix all versions 166 await execa('node', [bin, 'install', '--fix'], { cwd: projectRoot }); 167 168 // Check that the versions are fixed 169 pkg = await JsonFile.readAsync(path.resolve(projectRoot, 'package.json')); 170 171 // Didn't fix expo-auth-session since we didn't pass it in 172 expect(pkg.dependencies['expo-auth-session']).toBe('~3.6.1'); 173 }, 174 // Could take 45s depending on how fast npm installs 175 60 * 1000 176); 177