Repository: angular/angular.js
File: test/ng/rootScopeSpec.js (JavaScript)

Code before:
      $rootScope.$evalAsync(function() { this1 = this; });
      $rootScope.$digest();
      expect(this1).toEqual(this2);
    }));
  });


  describe('$apply', function() {
    it('should apply expression with full lifecycle', inject(function($rootScope) {
      var log = '';
      var child = $rootScope.$new();
      $rootScope.$watch('a', function(a) { log += '1'; });
      child.$apply('$parent.a=0');
      expect(log).toEqual('1');
    }));


    it('should catch exceptions', function() {
      module(function($exceptionHandlerProvider) {
        $exceptionHandlerProvider.mode('log');
      });
      inject(function($rootScope, $exceptionHandler, $log) {
        var log = '';
        var child = $rootScope.$new();
        $rootScope.$watch('a', function(a) { log += '1'; });
        $rootScope.a = 0;
        child.$apply(function() { throw new Error('MyError'); });
        expect(log).toEqual('1');
        expect($exceptionHandler.errors[0].message).toEqual('MyError');
        $log.error.logs.shift();
      });
    });


    it('should log exceptions from $digest', function() {
      module(function($rootScopeProvider, $exceptionHandlerProvider) {
        $rootScopeProvider.digestTtl(2);
        $exceptionHandlerProvider.mode('log');
      });
      inject(function($rootScope, $exceptionHandler) {
        $rootScope.$watch('a', function() {$rootScope.b++;});
        $rootScope.$watch('b', function() {$rootScope.a++;});
        $rootScope.a = $rootScope.b = 0;

        expect(function() {
          $rootScope.$apply();
        }).toThrow();

        expect($exceptionHandler.errors[0]).toBeDefined();

        expect($rootScope.$$phase).toBeNull();

Review comment (suggestion):
You could run a second round (it this and the subsequent tests) to make sure the listener will be called correctly and nothing will break after the initial run. E.g.:

```js
listener2.calls.reset();
listener3.calls.reset();

$rootScope.$broadcast('abc');
expect(listener2).toHaveBeenCalled();
expect(listener3).toHaveBeenCalled();
```

Code after:


        it('should call next listener after removing the current listener via its own handler', inject(function($rootScope) {
          var listener1 = jasmine.createSpy('listener1').and.callFake(function() { remove1(); });
          var remove1 = $rootScope.$on('abc', listener1);

          var listener2 = jasmine.createSpy('listener2');
          var remove2 = $rootScope.$on('abc', listener2);

          var listener3 = jasmine.createSpy('listener3');
          var remove3 = $rootScope.$on('abc', listener3);

          $rootScope.$broadcast('abc');
          expect(listener1).toHaveBeenCalled();
          expect(listener2).toHaveBeenCalled();
          expect(listener3).toHaveBeenCalled();

          listener1.calls.reset();
          listener2.calls.reset();
          listener3.calls.reset();

          $rootScope.$broadcast('abc');
          expect(listener1).not.toHaveBeenCalled();
          expect(listener2).toHaveBeenCalled();
          expect(listener3).toHaveBeenCalled();
        }));


        it('should call all subsequent listeners when a previous listener is removed via a handler', inject(function($rootScope) {
          var listener1 = jasmine.createSpy();
          var remove1 = $rootScope.$on('abc', listener1);

          var listener2 = jasmine.createSpy().and.callFake(remove1);
          var remove2 = $rootScope.$on('abc', listener2);

          var listener3 = jasmine.createSpy();
          var remove3 = $rootScope.$on('abc', listener3);

          $rootScope.$broadcast('abc');
          expect(listener1).toHaveBeenCalled();
          expect(listener2).toHaveBeenCalled();
          expect(listener3).toHaveBeenCalled();

          listener1.calls.reset();
          listener2.calls.reset();
          listener3.calls.reset();

          $rootScope.$broadcast('abc');
          expect(listener1).not.toHaveBeenCalled();
          expect(listener2).toHaveBeenCalled();
          expect(listener3).toHaveBeenCalled();