Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to run Protractor e2e tests with different environment variables (Angular 6)

In e2e test I want to have environment variable similar to the one as in Angular app. Looks like e2e test project builder "builder": "@angular-devkit/build-angular:protractor" does not support file replacements (unlike builder for web app):

"fileReplacements": [{
  "replace": "apps/web/src/environments/environment.ts",
  "with": "apps/web/src/environments/environment.prod.ts"
}]

So using Angular CLI ng e2e command I can switch configurations (or projects in angular.json) and in project configuration I can specify different Protractor configuration files protractor.conf.js. My question is: how can I get in e2e test any variable which will be different in configuration files or any other file, similar to the environment.ts file(s) in the app?

like image 999
Igor Avatar asked Sep 26 '18 18:09

Igor


2 Answers

So I ended up by using protractor.conf.js for environment variables. Main advantage - no need to use any third party lib.
1. Create a Protractor config file for each environment. In params specify your variables:

'protractor.conf.js'
...
  params: {
    postDeploy: false,
    credentials: {
      username: 'Test',
      password: 'Password'
    },
...

Second Protractor config file can use the first one as default and contain only overrides (to avoid duplicates in configs):

'protractor-dev.conf.js'

const defaultConfig = require('./protractor.conf');
let devConfig = {
  params: {
    postDeploy: true,
    credentials: {
      username: 'DevTest',
      password: 'DevPassword'
    }
  }
};
exports.config = Object.assign(defaultConfig.config, devConfig);
  1. Use configurations in angular.json for e2e project:
...
    "myproject-e2e": {
      "root": "",
      "sourceRoot": "",
      "projectType": "application",
      "architect": {
        "e2e": {
          "builder": "@angular-devkit/build-angular:protractor",
          "options": {
            "protractorConfig": "apps/myproject-e2e/protractor.conf.js",
            "devServerTarget": "myproject:serve"
          },
          "configurations": {
            "dev": {
              "protractorConfig": "apps/myproject-e2e/protractor-dev.conf.js",
              "devServerTarget": "",
              "baseUrl": "https://mywebsite.com"
            },
  1. Switch configurations in command line using -c or --configuration parameter
ng e2e myproject-e2e -c dev

You can also overwrite some parameters in the command line:

ng e2e myproject-e2e -c dev --baseUrl=https://mywebsite1.com

4. And finally in e2e tests itself, you can use any parameter from config:

browser.params.postDeploy
browser.params.credentials.username
like image 118
Igor Avatar answered Nov 03 '22 17:11

Igor


There are several parts to my answer so I'll try to keep them short as I can.

For environment variables I use a package called node-env-run. You can find it here. It is really easy to setup and allows you to change environment variables on the fly for local dev. You just setup an .env file in the directory you are running the commands from with a list of key/value pairs. Then you run your script like this:

nodenv --exec "ng e2e"

Nodenv will pickup the variables in that file and create the environment variables for the current test run. They are all discarded at the end of the session. It works great for local dev. From there I just setup environment variables in my build tool using the same env variable names as my .env file. Then I reference them in my params object in the protractor config. A good example would be for something like database credentials that may be different based on the environment you are running tests against.

params: {
    sql: {
        host: process.env.DB_HOST,
        dbname: process.env.DB_NAME,
        user: process.env.SQL_SERVER_USER,
        password: process.env.SQL_SERVER_PASSWORD
    }
}

For the second part of your question, how to implement different config files, I just use Object.assign(defaultConf, environmentConf) to accomplish this. The properties in the environmentConf will overwrite the same properties in the defaultConf.

I usually setup a default config that contains all the properties that are going to be common among all the configs. In the environment configs I only put properties that are unique to each environment. An example would be baseUrl. Here is an example of what I mean.

Default Config

module.exports = {
    allScriptsTimeout: 11000, // Timeout of each script
    specs: [
      '../specs/**/*.ts'
    ],
    capabilities: {
      browserName: 'chrome',
      acceptInsecureCerts : true,
      chromeOptions: {
        args: [
          '--start-maximized'
        ],
        prefs: {
          download: {
            prompt_for_download: false,
            default_directory: path.resolve(__dirname, '../downloads/')
          }
        }
      }
    },
    framework: 'jasmine', 
    jasmineNodeOpts: {
      showColors: true,
      defaultTimeoutInterval: 30000,
      print: function() {}
    },
    SELENIUM_PROMISE_MANAGER: false,
    onPrepare: function() {
        // ...
    },
    params: { // params object... },
    suites: { // suites listed here... }
}

Local Dev Config

module.exports = {
    baseUrl: 'http://localhost:8082',
    directConnect: true
}

QA Environment Config

module.exports = {
    baseUrl: 'https://my-app:7443',
    seleniumAddress: 'http://hub:4444/wd/hub',
    jasmineNodeOpts: {
        showColors: true,
        defaultTimeoutInterval: 60000,
        print: function() {}
    }
}

As you can see I'm using directConnect for local dev but a selenium hub once the app has been deployed to a given environment. The way I handle this with protractor is I pass in a command line flag --params.env=localhost. Then in the protractor config I get the value of this argument and choose what the config looks like based on that. Given the examples above it would look something like this.

From the command line I would run something similar to this nodenv --exec "ng e2e -- --params.env=localhost" or nodenv --exec "protractor conf.js --params.env=release". And here is what the conf.js would look like:

const env = process.argv.filter(arg => {
  return arg.includes('--params.env');
})[0];

let isLocal = false;
let isRelease = false;

if (env) {
  isLocal = env.includes('localhost');  
  isRelease = env.includes('release');
}

const defaultConf = require('./default.conf');
const localConf = require('./local.conf');
const qaEnvConf = require('./qa.conf');
const releaseConf = require('./release.conf');

let protractorConf = {};

// run tests against localhost
if (isLocal) {
  protractorConf = Object.assign(defaultConf, localConf);
}

if (isRelease) {
  protractorConf = Object.assign(defaultConf, releaseConf);
}

if(!isLocal && !isRelease) {
    protractorConf = Object.assign(defaultConf, qaEnvConf);
}

exports.config = protractorConf;

I'm sure there may be better ways to accomplish this but doing it this way allows me to use different configs without having to install another 3rd party dependency. Also, if you want to use the release configuration but run the tests against a staging or prod environment it's simple. Just pass in the baseUrl from the command line and it will overwrite anything that is in the config. nodenv --exec "ng e2e -- --baseUrl=https://my-url/ --params.env=release"

like image 39
tehbeardedone Avatar answered Nov 03 '22 15:11

tehbeardedone