Ilja Winokurow
Ilja Winokurow

Reputation: 39

What is the best way to manage different configurations in protractor?

We have an E2E project, that is written with Angular CLI 6 and Protractor. Currently we manage our configurations in angular.json:

 "configurations": {
            "local_chrome": {
              "protractorConfig": "./protractor.local.conf.js"
            },
            "remote_dev_chrome": {
              "protractorConfig": "./protractor.remote.dev.chrome.conf.js"
            },
            "remote_qsa_chrome": {
              "protractorConfig": "./protractor.remote.qsa.chrome.conf.js"
            },

In package.json we define scripts:

"e2e:local": "ng e2e --configuration=local_chrome --webdriver-update=false",
"e2e:dev:chrome": "ng e2e --configuration=remote_dev_chrome --webdriver-update=false",
"e2e:qsa:chrome": "ng e2e --configuration=remote_qsa_chrome --webdriver-update=false",

And so start we our tests from command line:

npm run e2e:qsa:chrome -- --base-url=https://aut.com --suite=szenarioTests

We want to test on 4 browsers and 5 environments. This means that we need 5*4=20 configuration files. And these files are 90% identical. That is a little bit monstrous and doesn't maintainable.

Does anyone know how can we minimize the number of configuration files and duplications? Are there any best practicies for E2E configuration in Angular CLI? Or maybe Protractor with Angular CLI is not suitable for big projects?

Upvotes: 1

Views: 1311

Answers (3)

Ilja Winokurow
Ilja Winokurow

Reputation: 39

We decided not to use angular cli for E2E tests. As result, we can configure our tests from command line and we have no dublications in config file (easy maintainability). In our solution we set many parameters per command line (browser, environment, logging level, remote or local). Our implementation is something similar to tehbeardedone solution. We have one base config file and one script that reads command line arguments and set corresponding configuration.
To do that we use

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

and then we use simple if to set configuration. For each parameter we have also default values.

Upvotes: 0

tehbeardedone
tehbeardedone

Reputation: 2858

I've done something similar to Ben's example except instead of extending I used Object.assign() to create my configs.

Create a default config that has all the things that will be shared between configurations. Then in each of the other configs only include the stuff that will be different between configurations. Any duplicate fields will get overwritten by the second argument in Object.assign()

For example, when I am running tests on my local dev environment this is what my local config looks like:

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

Our environment is all containerized so when I want to run smoke tests on builds this is what the config looks like:

// build.conf.js
module.exports = {
  baseUrl: 'https://app:7443',
  seleniumAddress: 'http://hub:4444/wd/hub',
  jasmineNodeOpts: {
    showColors: true,
    defaultTimeoutInterval: 30000,
    print: function() {}
  }
}

My default (shared) config doesn't include directConnect or seleniumAddress since they cannot be together in the same config. These values come from the other config files. Using the above examples as a reference your base config would look something like this:

const defaultConf = require('/path/to/dafault.conf');
const localConf = require('/path/to/local.conf');
const remoteDevChrome = require('/path/to/remote_dev_chrome.conf');
const remoteQsaChrome = require('/path/to/remote_qsa_chrome.conf');

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

let protractorConf = {};

switch (configuration.split('=')[1]) {
    case 'local_chrome':
        protractorConf = Object.assign(defaultConf, localConf);
    case 'remote_dev_chrome':
        protractorConf = Object.assign(defaultConf, remoteDevChrome);
    case 'remote_gsa_chrome':
        // ... and so on ...
    default:
       // whatever default config you use
}

exports.config = protractorConf;

Doing it this way you no longer need to have multiple scripts in your package.json. You can just use one script and the config will be built based off what you passed in for --configuration

Upvotes: 0

Ben Mohorc
Ben Mohorc

Reputation: 694

I am not sure of a way to minimize the total number of files. However, you can have 1 conf_shared file that all of your other confs extend which will make it easier to maintain in case of changes.

Example conf.js

import {ConfigShared} from "./conf_shared";
let specs = ["YourSpecsHere"];
let capabilities = {browserName:'chrome',...};

class ConfigLocal extends ConfigShared{
  specs = specs;
  capabilities = capabilities;
}
export const config = new ConfigLocal().exampleFunction(); //can call functions in conf_shared

Example conf_shared

import {Config} from 'protractor';

export class ConfigShared implements Config{
  specs: ['']; //will get overriden by conf.js
  framework: 'jasmine2';
  //rest of shared config goes here

  //you can also make function that you will call from conf.js
  exampleFunction(){
    //do stuff here
  }
}

Anything that you have shared between the conf files can go in a conf_shared file which will help with maintainability. If it is stuff that is only shared between certain tests, you can put it in functions (have functions change conf) and then call the functions from conf.js

Upvotes: 1

Related Questions