Davey
Davey

Reputation: 1093

How to test Angular JS directive with file upload and onchange event

I am about to give up on this. I have tried every which way to access the directive scope in a test.

'use strict';

angular.module('cmsModuleApp')
  .directive('fileUpload', function () {
    return {
      scope: {},  
      template: '<input type="file" >',
      restrict: 'E',
      controller: function fileUploadCtrl (scope) {
         //also tried scope.uploadFile here... 
         this.uploadFile = function (files) {console.log("please work...")};
      },
      link: function postLink(scope, element, attrs, Ctrl) {
          element.uploadFile = function (files) {console.log("pleaseeeee")};
      }
    };
  });

test::

'use strict';

describe('Directive: fileUpload', function () {

  beforeEach(module('cmsModuleApp'));

  var element;
  var scope;
  var files;

  beforeEach(inject(function ($rootScope) {
    scope = $rootScope.$new();
  }));

  it('should call a file upload method onchange event', inject(function ($compile) {

    element = angular.element('<file-upload></file-upload>');
    element = $compile(element)(scope);

    //tried moving this around thinking maybe it had to render or update
    scope.$digest();

    //Im loggin the element's scope to explore the object a bit
    console.log(element.scope()); 

    spyOn(element.scope(), 'uploadFile') 
    element.triggerHandler('onchange');

    expect(element.scope().uploadFile()).toHaveBeenCalled();

  }));
});

What I am trying to test is that when this file input changes (is clicked and loaded up with files) it will execute the uploadFile() method on the directive's scope. Once I get this working I was going to implement an $http service.

However, the method does not exist or is undefined.. No matter what I seem to try.

Upvotes: 0

Views: 2922

Answers (2)

Nick Litwin
Nick Litwin

Reputation: 3005

I think the issue might be that you are using an isolate scope scope: {}. Here's an example of how I did a similar task:

describe('File Input Directive', function() {
  var scope, element, isolateScope;

  beforeEach(function() {
    bard.appModule('appName');
    bard.inject(this, '$compile', '$rootScope');
    scope = $rootScope.$new();

    var html = '<form><my-file-input /></form>';
    var form = angular.element(html);
    element = form.find('my-file-input');
    var formElement = $compile(form)(scope);
    scope.$digest();

    isolateScope = element.isolateScope();
  });

  afterEach(function() {
    scope.$destroy();
  });

  bard.verifyNoOutstandingHttpRequests();

  describe('selectFile', function() {
    it('triggers a click on the file input', function() {
      var fileInput = $(element).find('.none')[0];
      var mockClick = sinon.spy(fileInput, 'click');

      isolateScope.selectFile();
      scope.$digest();

      expect(mockClick).calledOnce;
    });
  });

You can ignore all of the bard references - it's a helper library, which reduces some boilerplate. The important parts are creating the isolateScope in the beforeEach and referencing the directive's method (in this case, selectFile) on the isolateScope in the test itself. Also, notice the scope.$digest() after calling the method. Hope it helps!

Upvotes: 0

glepretre
glepretre

Reputation: 8167

Could you try to modify your test file like this?

I moved the variables declaration into the describe and the test initilization into the beforeEach. Then I created a spy on scope.uploadFile.

fileUpload_test :

'use strict';

describe('Directive: fileUpload', function () {
  var element;
  var scope;
  var files;

  beforeEach(module('cmsModuleApp'));

  beforeEach(inject(function ($rootScope) {
    scope = $rootScope.$new();
    element = angular.element('<file-upload></file-upload>');
    element = $compile(element)(scope);
    scope.$digest();
  }));

  afterEach(function() {
    scope.$destroy();
  });

  it('should call a file upload method onchange event', function() {
    scope.uploadFile = jasmine.createSpy();

    element.triggerHandler('change');

    expect(scope.uploadFile).toHaveBeenCalled();
  }));
});

Upvotes: 1

Related Questions