5
votes

I have a suite which includes multiple specs. Each spec uses code on some libraries that return a rejected promise upon failure.

I can easily catch those rejected promises inside my spec. What I'm wondering about is that if I can make Protractor exit the whole suite inside that catch function, because the next specs inside the same suite are dependent on the success of the previous specs.

Pretend I have a suite called testEverything which has these specs openApp,signIn,checkUser,logout. If openApp fails, all next specs will fail due to dependency.

Consider this code for openApp:

var myLib = require('./myLib.js');

describe('App', function() {

  it('should get opened', function(done) {

    myLib.openApp()
    .then(function() {

      console.log('Successfully opened app');

    })
    .catch(function(error) {

      console.log('Failed opening app');

      if ( error.critical ) {
        // Prevent next specs from running or simply quit test
      }

    })
    .finally(function() {

      done();

    });

  });

});

How would I exit the whole test?

2

2 Answers

1
votes

There is a module for npm called protractor-fail-fast. Install the module npm install protractor-fail-fast. Here's an example from their site where you would place this code into your conf file:

var failFast = require('protractor-fail-fast');

exports.config = {
  plugins: [{
    package: 'protractor-fail-fast'
  }],

  onPrepare: function() {
    jasmine.getEnv().addReporter(failFast.init());
  },

  afterLaunch: function() {
    failFast.clean(); // Cleans up the "fail file" (see below) 
  }  
}

Their url is here.

1
votes

I have managed to come up with a workaround. Now the actual code that I used is way more complex, but the idea is the same.

I added a global variable inside protractor's config file called bail. Consider the following code at the top of config file:

(function () {

  global.bail = false;

})();

exports.config: { ...

The above code uses an IIFE (Immediately Invoked Function Expression) which defines bail variable on protractor's global object (which would be available throughout the whole test).

I also have written asynchronous wrappers for the Jasmine matchers I need, which would execute an expect expression followed by a comparison, and return a promise (using Q module). Example:

var q = require('q');

function check(actual) {

    return {

      sameAs: function(expected) {

          var deferred = q.defer();
          var expectation = {};

          expect(actual).toBe(expected);

          expectation.result = (actual === expected);

          if ( expectation.result ) {

              deferred.resolve(expectation);
          }
          else {
              deferred.reject(expectation);
          }

          return deferred.promise;

      }

    };

}

module.exports = check;

Then at the end of each spec, I set the bail value based on the spec's progress, which would be determined by the promise of those asynchronous matchers. Consider the following as first spec:

var check = require('myAsyncWrappers'); // Whatever the path is

describe('Test', function() {

    it('should bail on next spec if expectation fails', function(done) {

        var myValue = 123;

        check(myValue).sameAs('123')
        .then(function(expectation) {

            console.log('Expectation was met'); // Won't happen

        })
        .catch(function(expectation) {

            console.log('Expectation was not met'); // Will happen

            bail = true; // The global variable

        })
        .finally(function() {

            done();

        });

    });

});

Finally, on the beginning of next specs, I check for bail and return if necessary:

describe('Test', function() {

    it('should be skipped due to bail being true', function(done) {

        if ( bail ) {

            console.log('Skipping spec due to previous failure');

            done();

            return;

        }

        // The rest of spec

    });

});

Now I want to mention that there's one module out there called protractor-fail-fast which bails on the whole test whenever an expectation fails.

But in my case, I needed to set that bail global variable depending on which type of expectation has been failed. I ended up writing a library (really small) that distinguishes failures as critical and non-critical and then using that, specs would be stopped only if a critical failure has occurred.