1import { vol, fs } from 'memfs'; 2 3import { createControlledEnvironment, getFiles } from '../env'; 4 5beforeEach(() => { 6 vol.reset(); 7}); 8 9const originalEnv = process.env; 10 11function resetEnv() { 12 process.env = originalEnv; 13 delete process.env.EXPO_NO_DOTENV; 14} 15 16beforeEach(() => { 17 resetEnv(); 18}); 19afterAll(() => { 20 resetEnv(); 21}); 22 23describe(getFiles, () => { 24 const originalError = console.error; 25 beforeEach(() => { 26 console.error = jest.fn(); 27 resetEnv(); 28 }); 29 afterEach(() => { 30 console.error = originalError; 31 }); 32 33 it(`gets development files`, () => { 34 expect(getFiles('development')).toEqual([ 35 '.env.development.local', 36 '.env.local', 37 '.env.development', 38 '.env', 39 ]); 40 }); 41 it(`gets production files`, () => { 42 expect(getFiles('production')).toEqual([ 43 '.env.production.local', 44 '.env.local', 45 '.env.production', 46 '.env', 47 ]); 48 }); 49 it(`gets test files`, () => { 50 // important 51 expect(getFiles('test')).toEqual(['.env.test.local', '.env.test', '.env']); 52 }); 53 it(`gets no files when dotenv is disabled`, () => { 54 process.env.EXPO_NO_DOTENV = '1'; 55 ['development', 'production', 'test'].forEach((mode) => { 56 expect(getFiles(mode)).toEqual([]); 57 }); 58 }); 59 60 it(`throws if NODE_ENV is not set`, () => { 61 getFiles(undefined); 62 63 expect(console.error).toBeCalledTimes(2); 64 expect(console.error).toBeCalledWith( 65 expect.stringContaining('The NODE_ENV environment variable is required but was not specified') 66 ); 67 }); 68 it(`throws if NODE_ENV is not valid`, () => { 69 expect(() => getFiles('invalid')).toThrowErrorMatchingInlineSnapshot( 70 `"Environment variable "NODE_ENV=invalid" is invalid. Valid values are "development", "test", and "production"` 71 ); 72 }); 73}); 74 75describe('get', () => { 76 beforeEach(() => { 77 resetEnv(); 78 }); 79 80 it(`memoizes`, () => { 81 delete process.env.FOO; 82 const envRuntime = createControlledEnvironment(); 83 vol.fromJSON( 84 { 85 '.env': 'FOO=default', 86 }, 87 '/' 88 ); 89 expect(envRuntime.get('/')).toEqual({ 90 env: { 91 FOO: 'default', 92 }, 93 files: ['/.env'], 94 }); 95 96 fs.writeFileSync('/.env', 'FOO=changed'); 97 98 expect(envRuntime.get('/')).toEqual({ 99 env: { 100 FOO: 'default', 101 }, 102 files: ['/.env'], 103 }); 104 expect(envRuntime.get('/', { force: true })).toEqual({ 105 env: { 106 FOO: 'changed', 107 }, 108 files: ['/.env'], 109 }); 110 }); 111}); 112describe('_getForce', () => { 113 beforeEach(() => { 114 resetEnv(); 115 }); 116 117 it(`returns the value of the environment variable`, () => { 118 delete process.env.FOO; 119 120 const envRuntime = createControlledEnvironment(); 121 vol.fromJSON( 122 { 123 '.env': 'FOO=bar', 124 }, 125 '/' 126 ); 127 128 expect(envRuntime._getForce('/')).toEqual({ 129 env: { 130 FOO: 'bar', 131 }, 132 files: ['/.env'], 133 }); 134 }); 135 136 it(`cascades env files (development)`, () => { 137 delete process.env.FOO; 138 process.env.NODE_ENV = 'development'; 139 const envRuntime = createControlledEnvironment(); 140 vol.fromJSON( 141 { 142 '.env': 'FOO=default', 143 '.env.local': 'FOO=default-local', 144 '.env.development': 'FOO=dev', 145 '.env.production': 'FOO=prod', 146 '.env.production.local': 'FOO=prod-local', 147 '.env.development.local': 'FOO=dev-local', 148 }, 149 '/' 150 ); 151 152 expect(envRuntime._getForce('/')).toEqual({ 153 env: { 154 FOO: 'dev-local', 155 }, 156 files: ['/.env.development.local', '/.env.local', '/.env.development', '/.env'], 157 }); 158 }); 159 160 it(`cascades env files (production)`, () => { 161 delete process.env.FOO; 162 process.env.NODE_ENV = 'production'; 163 const envRuntime = createControlledEnvironment(); 164 vol.fromJSON( 165 { 166 '.env': 'FOO=default', 167 '.env.local': 'FOO=default-local', 168 '.env.production': 'FOO=prod', 169 '.env.production.local': 'FOO=prod-local', 170 }, 171 '/' 172 ); 173 174 expect(envRuntime._getForce('/')).toEqual({ 175 files: ['/.env.production.local', '/.env.local', '/.env.production', '/.env'], 176 env: { 177 FOO: 'prod-local', 178 }, 179 }); 180 }); 181 182 it(`cascades env files (default)`, () => { 183 delete process.env.FOO; 184 const envRuntime = createControlledEnvironment(); 185 vol.fromJSON( 186 { 187 '.env': 'FOO=default', 188 '.env.local': 'FOO=default-local', 189 }, 190 '/' 191 ); 192 193 expect(envRuntime._getForce('/')).toEqual({ 194 files: ['/.env.local', '/.env'], 195 env: { 196 FOO: 'default-local', 197 }, 198 }); 199 }); 200 201 it(`skips modifying the environment with dotenv if disabled with EXPO_NO_DOTENV`, () => { 202 delete process.env.FOO; 203 process.env.EXPO_NO_DOTENV = '1'; 204 const envRuntime = createControlledEnvironment(); 205 vol.fromJSON( 206 { 207 '.env': 'FOO=default', 208 '.env.local': 'FOO=default-local', 209 }, 210 '/' 211 ); 212 213 expect(envRuntime._getForce('/')).toEqual({ env: {}, files: [] }); 214 }); 215 216 it(`does not return the env var if the initial the value of the environment variable`, () => { 217 const envRuntime = createControlledEnvironment(); 218 process.env.FOO = 'not-bar'; 219 220 vol.fromJSON( 221 { 222 '.env': 'FOO=bar', 223 }, 224 '/' 225 ); 226 227 expect(envRuntime._getForce('/')).toEqual({ env: {}, files: ['/.env'] }); 228 }); 229 230 it(`Does not fail when no files are available`, () => { 231 vol.fromJSON({}, '/'); 232 expect(createControlledEnvironment()._getForce('/')).toEqual({ 233 env: {}, 234 files: [], 235 }); 236 }); 237 238 it(`Does not assert on invalid env files`, () => { 239 vol.fromJSON( 240 { 241 '.env': 'ˆ˙•ª∆ø…ˆ', 242 }, 243 '/' 244 ); 245 246 expect(createControlledEnvironment()._getForce('/')).toEqual({ env: {}, files: ['/.env'] }); 247 }); 248}); 249