title: Jasmine Unit Test with postMessage and addEventListener
body: <p>I'm trying to unit test a situation with postMessage and addEventListener.  The use case is that I use a separate window for user logins similar to the OAuth workflow and then use postMessage in the login window to notify the main window that the user has logged in.  This is the listening code that would be in the main window:</p>
  
  <pre><code>$window.addEventListener("message", function(event) {
      if (event.data.type === "authLogin") {
          service.curUser = event.data.user;
          utilities.safeApply($rootScope);
          $window.postMessage({type: "authLoginSuccess"}, '*');
      }
  });
  </code></pre>
  
  <p>The utilities.safeApply function is defined as:</p>
  
  <pre><code>// Run $apply() if not already in digest phase.
  utilitiesService.safeApply = function(scope, fn) {
      return (scope.$$phase || scope.$root.$$phase) ? scope.$eval(fn) : scope.$apply(fn);
  };
  </code></pre>
  
  <p>My unit test is designed to send a postMessage to simulate the login:</p>
  
  <pre><code>describe('auth login postMessage', function() {
      var testUser = {handle: 'test'};
      beforeEach(function(done) {
          $window.postMessage({type: 'authLogin', user: testUser}, '*');
          function onAuthLoginSuccess(event) {
              $window.removeEventListener('message', onAuthLoginSuccess);
              done();
          }
          $window.addEventListener("message", onAuthLoginSuccess);
      });
      it("should set the user object", function() {
          expect(service.curUser).toEqual(testUser);
      });
  });
  </code></pre>
  
  <p>This is the result of running the unit test:</p>
  
  <pre><code>12 09 2015 14:10:02.952:INFO [launcher]: Starting browser Chrome
  12 09 2015 14:10:05.527:INFO [Chrome 45.0.2454 (Mac OS X 10.10.5)]: Connected on socket 537CxfI4xPnR0yjLAAAA with id 12583721
  Chrome 45.0.2454 (Mac OS X 10.10.5) ERROR
    Uncaught Error: Unexpected request: GET security/loginModal.tpl.html
    No more request expected
    at http://localhost:8089/__test/Users/userX/esupport/code/proj/public/vendor/angular-mocks/angular-mocks.js:250
  Chrome 45.0.2454 (Mac OS X 10.10.5): Executed 23 of 100 (skipped 60) ERROR (0.333 secs / 0.263 secs)
  </code></pre>
  
  <p>I'm at a loss of why it would try to load an HTML template.  I've narrowed it down so that if I don't call the $scope.$apply() function, the unit test succeeds without error.  However, I need that $apply() to update the view.</p>
  
  <p>I've tried stubbing out the utilities.safeApply method in the unit test and also tried to set up an expectation for the HTML template GET request.  These attempts look like:</p>
  
  <pre><code>describe('auth login postMessage', function() {
      var testUser = {handle: 'test'};
      beforeEach(function(done) {
          $httpBackend.when('GET', 'security/loginModal.tpl.html').respond('');  // &lt;-- NEW
          spyOn(utilities, 'safeApply').and.callFake(angular.noop);  // &lt;-- NEW
          window.postMessage({type: 'authLogin', user: testUser}, '*');
          function onAuthLoginSuccess(event) {
              window.removeEventListener('message', onAuthLoginSuccess);
              done();
          }
          window.addEventListener("message", onAuthLoginSuccess);
      });
      it("should set the user object", function() {
          expect(service.curUser).toEqual(testUser);
      });
  });
  </code></pre>
  
  <p>Both of these attempts do nothing.  I still get the same error message.  I've tried other debugging steps such as using spyOn for $location.path() so it returns a sample value like "/fake".  In all other unit tests where I'm directly testing service methods and not using postMessage to trigger the service code, the stubbing works fine.  However, in the addEventListener function, the $location.path() returns "" which points towards a theory that the addEventListener function is running in an entirely different instance than what the unit test has prepared.  That would explain why the stubbed out functions aren't being used and why this other instance is trying to load a bogus template.  This <a href="https://stackoverflow.com/questions/1338599/the-value-of-this-within-the-handler-using-addeventlistener">discussion</a> also solidifies the theory.</p>
  
  <p>So now the question is, how do I get it to work?  i.e, how do I get the addEventListener function to run in the same instance where my stubbed functions are used and it doesn't make a request to the HTML template?</p>