christopherthielen / ui-router-extras

THIS PROJECT IS NO LONGER MAINTAINED -- Extras for UI-Router for AngularJS. Sticky States (a.k.a. parallel states), Deep State Redirect (for tab-like navigation), Future States (async state definition)
http://christopherthielen.github.io/ui-router-extras/
MIT License
916 stars 211 forks source link

ui-router-extras breaks my unit testing? #127

Closed matthewharwood closed 9 years ago

matthewharwood commented 9 years ago

QUESTION:

- Why are my tests failing when ui-router-extras (not normal ui-router) is install?

- How can I use ui-router-extras and still have my tests pass?


If you want to install this quickly use yeoman + angular-fullstack-generator + bower install ui-router-extras

I found a similar issue with normal ui-router.

Error log

If I uninstall ui-router.extras it this test passes just fine: enter image description here

UPDATED for beforeEach module of $urlRouterProvider TEST Heres my test:

'use strict';

describe('Controller: MainCtrl', function () {

  // load the controller's module
  beforeEach(module('morningharwoodApp'));
  beforeEach(module('socketMock'));

  var MainCtrl,
      scope,
      $httpBackend;

  // Initialize the controller and a mock scope

  beforeEach(
    inject( function (_$httpBackend_, $controller, $rootScope) {
      $httpBackend = _$httpBackend_;
      $httpBackend.expectGET('/api/things')
        .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']);

      scope = $rootScope.$new();
      MainCtrl = $controller('MainCtrl', {
        $scope: scope
      });
    }),
    module(function ($urlRouterProvider) {
      $urlRouterProvider.otherwise( function(){ return false; });
    })
  );

  it('should attach a list of things to the scope', function () {
    $httpBackend.flush();
    expect(scope.awesomeThings.length).toBe(4);
  });
});

Here's my karma.conf

module.exports = function(config) {
  config.set({
    // base path, that will be used to resolve files and exclude
    basePath: '',

    // testing framework to use (jasmine/mocha/qunit/...)
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'client/bower_components/jquery/dist/jquery.js',
      'client/bower_components/angular/angular.js',
      'client/bower_components/angular-mocks/angular-mocks.js',
      'client/bower_components/angular-resource/angular-resource.js',
      'client/bower_components/angular-cookies/angular-cookies.js',
      'client/bower_components/angular-sanitize/angular-sanitize.js',
      'client/bower_components/lodash/dist/lodash.compat.js',
      'client/bower_components/angular-socket-io/socket.js',
      'client/bower_components/angular-ui-router/release/angular-ui-router.js',
      'client/bower_components/famous-polyfills/classList.js',
      'client/bower_components/famous-polyfills/functionPrototypeBind.js',
      'client/bower_components/famous-polyfills/requestAnimationFrame.js',
      'client/bower_components/famous/dist/famous-global.js',
      'client/bower_components/famous-angular/dist/famous-angular.js',
      'client/app/app.js',
      'client/app/app.coffee',
      'client/app/**/*.js',
      'client/app/**/*.coffee',
      'client/components/**/*.js',
      'client/components/**/*.coffee',
      'client/app/**/*.jade',
      'client/components/**/*.jade',
      'client/app/**/*.html',
      'client/components/**/*.html'
    ],

    preprocessors: {
      '**/*.jade': 'ng-jade2js',
      '**/*.html': 'html2js',
      '**/*.coffee': 'coffee',
    },

    ngHtml2JsPreprocessor: {
      stripPrefix: 'client/'
    },

    ngJade2JsPreprocessor: {
      stripPrefix: 'client/'
    },

    // list of files / patterns to exclude
    exclude: [],

    // web server port
    port: 8080,

    // level of logging
    // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // Start these browsers, currently available:
    // - Chrome
    // - ChromeCanary
    // - Firefox
    // - Opera
    // - Safari (only Mac)
    // - PhantomJS
    // - IE (only Windows)
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, it capture browsers, run tests and exit
    singleRun: false
  });
};
christopherthielen commented 9 years ago

This unit test fails as soon as the $state service is initialized. ui-router-extras initializes the $state service, which is why inclusion is breaking your unit test.

If you do not include ui-router-extras, adding the following line in app.js will bootstrap $state and cause your test to fail: app.run(function($state){}) This isn't really specific to ui-router-extras.

christopherthielen commented 9 years ago

UI-Router is synchronizing the browser url with the proper state when it bootstraps. It is matching the 'main' state by default and, thus triggers the transition and the fetch of main's template, which $httpBackend is complaining about.

$urlRouterProvider.deferIntercept(); will cause ui-router to delay url <=> state synchronization until you call $urlRouterProvider.listen(). Put that in your unit test initialization code and you should be good to go.

@matthewharwood please try this and verify if this works for you.

matthewharwood commented 9 years ago

Hey @christopherthielen I'm a super Noob with testing(literally never have written them). That being said, I tried to add $urlRouterProvider.deferIntercept(); and it threw the error saying Error: Unexpected request: GET app/main/main.html Here's how I did it

  beforeEach(
    inject( function (_$httpBackend_, $controller, $rootScope) {

      $httpBackend = _$httpBackend_;
      $httpBackend.expectGET('/api/things')
        .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']);

      scope = $rootScope.$new();
      MainCtrl = $controller('MainCtrl', {
        $scope: scope
      });
    }),
    module(function ($urlRouterProvider) {
      $urlRouterProvider.deferIntercept();

    })
  );

On a lighter note, I was able to fix this from a 200 point bounty on stackoverflow Stack Question. but I don't know if this is the intended method. It honestly feels more like a hack (but again I have no idea whats going on).

  beforeEach(
    inject( function (_$httpBackend_, $controller, $rootScope,  _$state_) {
     //injecting state and spyOn(state,go)/transitionTo fixes this GET error.
      state = _$state_;
      spyOn( state, 'go' );
      spyOn( state, 'transitionTo' );
      $httpBackend = _$httpBackend_;
      $httpBackend.expectGET('/api/things')
        .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']);

      scope = $rootScope.$new();
      MainCtrl = $controller('MainCtrl', {
        $scope: scope
      });
    }),
    module(function ($urlRouterProvider) {
      $urlRouterProvider.deferIntercept();

    })
  );

Could you explain why? Moreover, if there is a better method could you answer the stack so I can mark it correct? #forthefuture

christopherthielen commented 9 years ago

Here's what I changed the controller spec file from angular-fullstack to:

describe('Controller: MainCtrl', function () {

  // load the controller's module
  beforeEach(module('uiRouterExtrasKarmaBugApp'));

  beforeEach(module(function($urlRouterProvider) {
    $urlRouterProvider.deferIntercept();
  }));

  var MainCtrl,
      scope,
      $httpBackend;

  // Initialize the controller and a mock scope
  beforeEach(inject(function (_$httpBackend_, $controller, $rootScope) {
    $httpBackend = _$httpBackend_;
    $httpBackend.expectGET('/api/things')
      .respond(['HTML5 Boilerplate', 'AngularJS', 'Karma', 'Express']);

    scope = $rootScope.$new();
    MainCtrl = $controller('MainCtrl', {
      $scope: scope
    });
  }));

  it('should attach a list of things to the scope', function () {
    $httpBackend.flush();
    expect(scope.awesomeThings.length).toBe(4);
  });
});
christopherthielen commented 9 years ago

@matthewharwood can you check out this comment and let me know if it helps you?

https://github.com/angular-ui/ui-router/issues/212#issuecomment-69974072

christopherthielen commented 9 years ago

closing for inactivity