1
votes

I try to create e2e tests with karma and jasmine with yeoman. In my karma-e2e.conf.js I add jasmine:

files = [
   JASMINE,
   JASMINE_ADAPTER,
   ANGULAR_SCENARIO,
   ANGULAR_SCENARIO_ADAPTER,
   'test/e2e/**/*.js'
];

A need async testing so I need to use runs, waits, waitsFor (https://github.com/pivotal/jasmine/wiki/Asynchronous-specs)

But if I try to use it:

it('test', function () {
    runs(function () {
        ...
    });
});

Scenatio test runner returns this:

TypeError: Cannot call method 'runs' of null
    at runs (http://localhost:8080/adapter/lib/jasmine.js:562:32)
    at Object.<anonymous> (http://localhost:8080/base/test/e2e/eduUser.js:42:3)
    at Object.angular.scenario.SpecRunner.run   (http://localhost:8080/adapter/lib/angular-scenario.js:27057:15)
    at Object.run (http://localhost:8080/adapter/lib/angular-scenario.js:10169:18)

I don't know where the problem is. Can you help me please?

1

1 Answers

8
votes

Angular e2e tests with Karma don't and can't use the JASMINE adapter. Instead you have the ANGULAR_SCENARIO_ADAPTER which has a similar feel to writing Jasmine tests.

All commands in the adapter's API are asynchronous anyway. For example element('#nav-items').count() doesn't return a number, it returns a Future object. Future objects are placed in a queue and executed asynchronously as the runner progresses. To quote the API docs:

expect(future).{matcher}:

[...] All API statements return a future object, which get a value assigned after they are executed.

If you need to run your own asynchronous test code, you can extend the adapter's DSL, this is easier than it might sound. The idea is that you return your own Future which can be evaluated by a matcher such as toBe(). There are some examples on how to do this in the e2e-tests.js Gist from Vojta. Just remember to call done(null, myRetrunValue); when your test code is successful (myRetrunValue is the value evaluated by your matcher). Or done('Your own error message'); if you want the test to fail.

UPDATE: In response to question below. To simulate a login, first add a function called login to the dsl:

angular.scenario.dsl('login', function() {
  return function(selector) {
    
    // @param {DOMWindow} appWindow The window object of the iframe (the application)
    // @param {jQuery} $document jQuery wrapped document of the application
    // @param {function(error, value)} done Callback that should be called when done
    //                                      (will basically call the next item in the queuue)
    return this.addFutureAction('Logging in', function(appWindow, $document, done) {

      // You can do normal jQuery/jqLite stuff here on $document, just call done() when your asynchronous tasks have completed
      
      // Create some kind of listener to handle when your login is complete
      $document.one('loginComplete', function(e){
        done(null, true);
      }).one('loginError', function(e){
        done('Login error', false);
      });
      
      // Simulate the button click
      var loginButton = $document.find(selector || 'button.login');
      loginButton.click();
    })
  };
});

And then call:

beforeEach( function()
{
    expect( login('button.login') ).toBeTruthy();
});