Zach Lysobey
Zach Lysobey

Reputation: 15744

Conditionally ignore individual tests with Karma / Jasmine

I have some tests that fail in PhantomJS but not other browsers.

I'd like these tests to be ignored when run with PhantomJS in my watch task (so new browser windows don't take focus and perf is a bit faster), but in my standard test task and my CI pipeline, I want all the tests to run in Chrome, Firefox, etc...

I've considered a file-naming convention like foo.spec.dont-use-phantom.js and excluding those in my Karma config, but this means that I will have to separate out the individual tests that are failing into their own files, separating them from their logical describe blocks and having more files with weird naming conventions would generally suck.

In short:

Is there a way I can extend Jasmine and/or Karma and somehow annotate individual tests to only run with certain configurations?

Upvotes: 28

Views: 35008

Answers (5)

justCurious
justCurious

Reputation: 977

Just rename the tests that you want to disable from it(...) to xit(...)

function xit: A temporarily disabled it. The spec will report as pending and will not be executed.

Upvotes: 1

milanlempera
milanlempera

Reputation: 2263

Jasmine supports a pending() function.

If you call pending() anywhere in the spec body, no matter the expectations, the spec will be marked pending.

You can call pending() directly in test, or in some other function called from test.

function skipIfCondition() {
  pending();
}

function someSkipCheck() {
  return true;
}

describe("test", function() {
  it("call pending directly by condition", function() {
    if (someSkipCheck()) {
      pending();
    }

    expect(1).toBe(2);
  });

  it("call conditionally skip function", function() {
    skipIfCondition();

    expect(1).toBe(3);
  });

  it("is executed", function() {
    expect(1).toBe(1);
  });

});

working example here: http://plnkr.co/edit/JZtAKALK9wi5PdIkbw8r?p=preview

I think it is purest solution. In test results you can see count of finished and skipped tests.

Upvotes: 35

Marcin Rapacz
Marcin Rapacz

Reputation: 646

Try this. I am using this solution in my projects.

it('should do something', function () {
    if (!/PhantomJS/.test(window.navigator.userAgent)) {
        expect(true).to.be.true;
    }
});

This will not run this particular test in PhantomJS, but will run it in other browsers.

Upvotes: 2

Michael Radionov
Michael Radionov

Reputation: 13319

The most simple solution that I see is to override global functions describe and it to make them accept third optional argument, which has to be a boolean or a function returning a boolean - to tell whether or not current suite/spec should be executed. When overriding we should check if this third optional arguments resolves to true, and if it does, then we call xdescribe/xit (or ddescribe/iit depending on Jasmine version), which are Jasmine's methods to skip suite/spec, istead of original describe/it. This block has to be executed before the tests, but after Jasmine is included to the page. In Karma just move this code to a file and include it before test files in karma.conf.js. Here is the code:

(function (global) {

  // save references to original methods
  var _super = {
    describe: global.describe,
    it: global.it
  };

  // override, take third optional "disable"
  global.describe = function (name, fn, disable) {
    var disabled = disable;
    if (typeof disable === 'function') {
      disabled = disable();
    }

    // if should be disabled - call "xdescribe" (or "ddescribe")
    if (disable) {
      return global.xdescribe.apply(this, arguments);
    }

    // otherwise call original "describe"
    return _super.describe.apply(this, arguments);
  };

  // override, take third optional "disable"
  global.it = function (name, fn, disable) {
    var disabled = disable;
    if (typeof disable === 'function') {
      disabled = disable();
    }

    // if should be disabled - call "xit" (or "iit")
    if (disable) {
      return global.xit.apply(this, arguments);
    }

    // otherwise call original "it"
    return _super.it.apply(this, arguments);
  };

}(window));

And usage example:

describe('foo', function () {

  it('should foo 1 ', function () {
    expect(true).toBe(true);
  });

  it('should foo 2', function () {
    expect(true).toBe(true);
  }); 

}, true); // disable suite

describe('bar', function () {

  it('should bar 1 ', function () {
    expect(true).toBe(true);
  });

  it('should bar 2', function () {
    expect(true).toBe(true);
  }, function () {
    return true; // disable spec
  });

}); 

See a working example here

I've also stumbled upon this issue where the idea was to add a chain method .when() for describe and it, which will do pretty much the same I've described above. It may look nicer but is a bit harder to implement.

describe('foo', function () {

  it('bar', function () {
    // ...
  }).when(anything);      

}).when(something);

If you are really interested in this second approach, I'll be happy to play with it a little bit more and try to implement chain .when().

Update:

Jasmine uses third argument as a timeout option (see docs), so my code sample is replacing this feature, which is not ok. I like @milanlempera and @MarcoCI answers better, mine seems kinda hacky and not intuitive. I'll try to update my solution anyways soon not to break compatibilty with Jasmine default features.

Upvotes: 11

MarcoL
MarcoL

Reputation: 9989

I can share my experience with this.

In our environment we have several tests running with different browsers and different technologies. In order to run always the same suites on all the platforms and browsers we have a helper file loaded in karma (helper.js) with some feature detection functions loaded globally.

I.e.

function isFullScreenSupported(){
  // run some feature detection code here
}

You can use also Modernizr for this as well.

In our tests then we wrap things in if/else blocks like the following:

it('should work with fullscreen', function(){
  if(isFullScreenSupported()){
    // run the test
  }
  // don't do anything otherwise
});

or for an async test

it('should work with fullscreen', function(done){
  if(isFullScreenSupported()){
    // run the test
    ...
    done();
  } else {
    done();
  }
});

While it's a bit verbose it will save you time for the kind of scenario you're facing.

In some cases you can use user agent sniffing to detect a particular browser type or version - I know it is bad practice but sometimes there's effectively no other way.

Upvotes: 8

Related Questions