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