diff --git a/packages/jest-util/src/__tests__/create_process_object.test.js b/packages/jest-util/src/__tests__/create_process_object.test.js index 1417c5fed9d4..0c5c2036c616 100644 --- a/packages/jest-util/src/__tests__/create_process_object.test.js +++ b/packages/jest-util/src/__tests__/create_process_object.test.js @@ -30,3 +30,47 @@ it('creates a process object that looks like the original one', () => { it('fakes require("process") so it is equal to "global.process"', () => { expect(require('process') === process).toBe(true); }); + +it('checks that process.env works as expected on Linux platforms', () => { + Object.defineProperty(process, 'platform', {get: () => 'linux'}); + + // Existing properties inside process.env are copied to the fake environment. + process.env.PROP_STRING = 'foo'; + process.env.PROP_NUMBER = 3; + process.env.PROP_UNDEFINED = undefined; + + const fake = createProcessObject().env; + + // All values converted to strings. + expect(fake.PROP_STRING).toBe('foo'); + expect(fake.PROP_NUMBER).toBe('3'); + expect(fake.PROP_UNDEFINED).toBe('undefined'); + + // Mac and Linux are case sensitive. + expect(fake.PROP_string).toBe(undefined); + + // Added properties to the fake object are not added to the real one. + fake.PROP_ADDED = 'new!'; + + expect(fake.PROP_ADDED).toBe('new!'); + expect(process.env.PROP_ADDED).toBe(undefined); +}); + +it('checks that process.env works as expected in Windows platforms', () => { + Object.defineProperty(process, 'platform', {get: () => 'win32'}); + + // Windows is not case sensitive when it comes to property names. + process.env.PROP_STRING = 'foo'; + + const fake = createProcessObject().env; + + expect(fake.PROP_STRING).toBe('foo'); + expect(fake.PROP_string).toBe('foo'); + + // Inherited methods, however, are not affected by case insensitiveness. + expect(typeof fake.toString).toBe('function'); + expect(typeof fake.valueOf).toBe('function'); + + expect(typeof fake.tostring).toBe('undefined'); + expect(typeof fake.valueof).toBe('undefined'); +}); diff --git a/packages/jest-util/src/create_process_object.js b/packages/jest-util/src/create_process_object.js index 6b6f25fe2bbc..d31c184e5556 100644 --- a/packages/jest-util/src/create_process_object.js +++ b/packages/jest-util/src/create_process_object.js @@ -9,7 +9,43 @@ import deepCyclicCopy from './deep_cyclic_copy'; -const BLACKLIST = new Set(['mainModule', '_events']); +const BLACKLIST = new Set(['env', 'mainModule', '_events']); + +// The "process.env" object has a bunch of particularities: first, it does not +// directly extend from Object; second, it converts any assigned value to a +// string; and third, it is case-insensitive in Windows. We use a proxy here to +// mimic it (see https://nodejs.org/api/process.html#process_process_env). + +function createProcessEnv() { + // $FlowFixMe: Apparently Flow does not understand that this is a prototype. + const proto: Object = Object.getPrototypeOf(process.env); + const real = Object.create(proto); + const lookup = {}; + + const proxy = new Proxy(real, { + get(target, key) { + if ((typeof key === 'string') && (process.platform === 'win32')) { + return lookup[key in proto ? key : key.toLowerCase()]; + } else { + return real[key]; + } + }, + + set(target, key, value) { + const strValue = '' + value; + + if (typeof key === 'string') { + lookup[key.toLowerCase()] = strValue; + } + + real[key] = strValue; + + return true; + }, + }); + + return Object.assign(proxy, process.env); +} export default function() { const process = require('process'); @@ -29,5 +65,7 @@ export default function() { } } + newProcess.env = createProcessEnv(); + return newProcess; }