morficus / cypress-dotenv

Cypress plugin that enables compatability with dotenv
53 stars 13 forks source link

Update plugin to be compatible with Cypress 10 #29

Open admah opened 2 years ago

admah commented 2 years ago

Hello 👋 I'm writing on behalf of the Cypress DX team. We wanted to notify you that some changes may need to be made to this plugin to ensure that it works in Cypress 10.

For more information, here is our official plugin update guide.

paulsheldrake commented 2 years ago

For those, who are just migrating and want something that will work you can move the plugin code inside the cypress config

You will need to add camelcase no later than 6.3 as version 7 switches to ESM and breaks in cypress. npm i camelcase@6.3.0 --dev

const { defineConfig } = require("cypress");

module.exports = defineConfig({
  reporter: "cypress-multi-reporters",

  reporterOptions: {
    configFile: "reporter-config.json",
  },

  e2e: {
    setupNodeEvents(on, config) {
      // cypress-dotenv doesnt work with cypress 10 so moving plugin code here.
      const cypressPrefix = 'CYPRESS_'

      // load the content of the .env file, then parse each variable to the correct type (string, number, boolean, etc.)
      let envVars = require('dotenv').config()

      // if no env vars were parsed, then there is nothing to do here (most likely an empty or non-existing .env file)
      if (envVars.parsed === undefined) {
        return config
      }

      const dotenvParseVariables = require('dotenv-parse-variables')
      envVars = dotenvParseVariables(envVars.parsed)

      // get the name of all env vars that relate to cypress
      const cypressEnvVarKeys = false
        ? Object.keys(envVars)
        : Object.keys(envVars).filter((envName) => envName.startsWith(cypressPrefix))
      const camelCase = require("camelcase");
      cypressEnvVarKeys.forEach((originalName) => {
        const pattern = new RegExp(`^${cypressPrefix}`, 'g')
        const cleanName = originalName.replace(pattern, '')
        const camelCaseName = camelCase(cleanName)
        const parsedEnvar = envVars[originalName]
        const processEnvVar = process.env[originalName]
        const envVar = typeof parsedEnvar === 'string' ? processEnvVar : parsedEnvar

        config.env[cleanName] = envVar

        if (config.hasOwnProperty(camelCaseName) && camelCaseName !== 'env') {
          config[camelCaseName] = envVar
        }
      })

      return config
    },
  },
});
jasperkennis commented 2 years ago

@paulsheldrake I had some issues with your suggested solution. For reasons that I did not dig deeper into, the camelcase stuff was causing my whole config file to become unrecognised.

For anyone reading this and having that same problem, for me Pauls solution worked after I removed these parts:

const camelCaseName = camelCase(cleanName)
# and
if (config.hasOwnProperty(camelCaseName) && camelCaseName !== 'env') {
  config[camelCaseName] = envVar
}

Obviously that bit is there for a reason so double check that relevant envs continue to work.

clowerweb commented 2 years ago

Simpler solution:

yarn add -D dotenv dotenv-parse-variables

setupNodeEvents(on, config) {
    let envVars = require('dotenv').config({path: 'path/to/.env'});

    if (envVars.parsed === undefined) {
        return config;
    }

    const dotenvParseVariables = require('dotenv-parse-variables');
    envVars = dotenvParseVariables(envVars.parsed);

    const cypressEnvVarKeys = Object.keys(envVars);

    cypressEnvVarKeys.forEach((envVar) => {
        config[envVar] = process.env[envVar];
    });

    return config;
}

Usage: Cypress.config('MY_VARIABLE_NAME')

1r00t commented 2 years ago

This is my solution. I use different config files so I added the snipped from: https://docs.cypress.io/api/plugins/configuration-api#Switch-between-multiple-configuration-files

cypress.config.js

const { defineConfig } = require('cypress');

const fs = require('fs-extra');
const path = require('path');

function getConfigurationByFile(file) {
  const pathToConfigFile = path.resolve('config', `${file}.json`);
  return fs.readJson(pathToConfigFile);
}

module.exports = defineConfig({
  e2e: {
    setupNodeEvents(on, config) {
      const dotenvPlugin = require('cypress-dotenv');
      const file = config.env.configFile || 'mydefaultconfig.dev';
      return getConfigurationByFile(file).then((configuration) => dotenvPlugin(configuration));
    },
  },
});
caseyfw commented 2 years ago

Just a note for Cypress 10 users who end up here: if you're looking to use dotenv to stash customisable values that a user may want to change, consider using the cypress.env.json mechanism instead.

The docs even recommend using this file and adding it to your .gitignore as a way of injecting customisable config into your tests.

alextoul commented 1 year ago

I just did this and it worked

 setupNodeEvents(on, config: any) {
      let envVars = require('dotenv').config({ path: '../.env' })
      // Pick the ones I want
      config.env.HOST_URL = envVars.parsed.HOST_URL
      config.env.ANOTHER_NAME = envVars.parsed.ANOTHER_NAME
      return config
    },

What am I missing here?

One thing I was not able to achieve is to set baseUrl to an envVars value. If you know a trick, let me know! Thanks.

savchukm commented 1 year ago

Hey! I'm using Cypress 10.7.0 with cypress-dotenv and looks like no extra changes needed in plugin source code. Only missing part here is clear docs (?) According to Cypress migration guide:

The setupNodeEvents() config option is functionally equivalent to the function exported from the plugins file;

In order to get it working you need to rewrite your config instead of having cypress/plugins/index.js:

import { defineConfig } from 'cypress'
import dotenvPlugin from 'cypress-dotenv'

export default defineConfig({
    e2e: {
                ...
        setupNodeEvents(on, config) {
            return dotenvPlugin(config)
        },
    },
    ...
})