runtime-env / import-meta-env

Build once, deploy anywhere.
http://import-meta-env.org/
MIT License
193 stars 13 forks source link

Re-assign environment variable in a Jest test #1251

Open luca-gr4vy opened 2 months ago

luca-gr4vy commented 2 months ago

Is there a way to reassign environment variables in a Jest test?

I'm using CRA v5 with @import-meta-env/babel, craco (not having to eject the app) and the babel transform function from https://github.com/runtime-env/import-meta-env/blob/a356a958f5e1e9f11d8852d424a6f2b37abc736e/packages/examples/create-react-app-example/config/jest/babelTransform.js

For example:

const hasFeature = () => {
  if (import.meta.env.REACT_APP_FEATURE === 'true') {
    return true;
  }
  return false;
}

with a test:

test('should return true if feature is enabled', () => {
  import.meta.env.REACT_APP_FEATURE = 'true' // <-- this value assignment errors out
  expect(hasFeature()).toBe(true)
})

test('should return false if feature is not enabled', () => {
  import.meta.env.REACT_APP_FEATURE = 'false'
  expect(hasFeature()).toBe(false)
})

The error I'm getting is:

Property left of AssignmentExpression expected node to be of a type ["LVal","OptionalMemberExpression"] but instead got "StringLiteral"
      at Object.validate (node_modules/@babel/core/node_modules/@babel/types/src/definitions/utils.ts:139:11)
      at validateField (node_modules/@babel/core/node_modules/@babel/types/src/validators/validate.ts:32:9)
      at validate (node_modules/@babel/core/node_modules/@babel/types/src/validators/validate.ts:19:3)
      at NodePath._replaceWith (node_modules/@babel/core/node_modules/@babel/traverse/src/path/replacement.ts:217:5)
      at NodePath.replaceWith (node_modules/@babel/core/node_modules/@babel/traverse/src/path/replacement.ts:190:8)
      ...

I could do that with process.env and it worked fine.

Vitest has stubEnv (https://vitest.dev/api/vi#vi-stubenv), looking for similar functionality

luca-gr4vy commented 2 months ago

Solved by using a separate file to export all the env variables and reassign / mock inline, for example:

// env.js
export const env = {
  REACT_APP_FEATURE: import.meta.env.REACT_APP_FEATURE
}
import { env } from './env'

test('should return true if feature is enabled', () => {
  env.REACT_APP_FEATURE = 'true'
  expect(hasFeature()).toBe(true)
})

test('should return false if feature is not enabled', () => {
  env.REACT_APP_FEATURE = 'false'
  expect(hasFeature()).toBe(false)
})

It's a bit of a workaround, but fine in my case

soc221b commented 2 months ago

Unfortunately, since environment variables are statically replaced during compilation (with compile-time mode), they cannot be changed after a compile-time transformation.

If you're still in the early stages of integrating import-meta-env, maybe you can try another package: https://github.com/runtime-env/runtime-env

Here is an example based on https://github.com/runtime-env/runtime-env/tree/main/examples/test folder:

.runtimeenvschema.json

{
  "type": "object",
  "properties": {
    "HAS_FEATURE": {
      "type": "boolean"
    }
  },
  "required": ["HAS_FEATURE"]
}

.env

HAS_FEATURE=true

src/index.js

module.exports = function hasFeature() {
  return globalThis.runtimeEnv.HAS_FEATURE;
};

__tests__/index.test.js

const hasFeature = require("../src");

test("true", () => {
  globalThis.runtimeEnv.HAS_FEATURE = true;
  expect(hasFeature()).toBe(true);
});

test("false", () => {
  globalThis.runtimeEnv.HAS_FEATURE = false;
  expect(hasFeature()).toBe(false);
});