Monday, May 20, 2024
 Popular · Latest · Hot · Upcoming
35
rated 0 times [  41] [ 6]  / answers: 1 / hits: 26998  / 10 Years ago, sat, august 2, 2014, 12:00:00

I'm having difficulties with a unit test in which I want to verify the processing of a file, which would usually be selected in the view via <input type='file'>.



In the controller part of my AngularJS app the file is processed inside the input's change event like so:



//bind the change event of the file input and process the selected file
inputElement.on(change, function (evt) {
var fileList = evt.target.files;
var selectedFile = fileList[0];
if (selectedFile.size > 500000) {
alert('File too big!');
// ...


I'd like evt.target.files to contain my mock data instead of the user's selected file in my unit test. I realized that I can't instantiate a FileList and File object by myself, which would be the according objects the browser is working with. So I went with assigning a mock FileList to the input's files property and triggering the change event manually:



describe('document upload:', function () {
var input;

beforeEach(function () {
input = angular.element(<input type='file' id='file' accept='image/*'>);
spyOn(document, 'getElementById').andReturn(input);
createController();
});

it('should check file size of the selected file', function () {
var file = {
name: test.png,
size: 500001,
type: image/png
};

var fileList = {
0: file,
length: 1,
item: function (index) { return file; }
};

input.files = fileList; // assign the mock files to the input element
input.triggerHandler(change); // trigger the change event

expect(window.alert).toHaveBeenCalledWith('File too big!');
});


Unfortunately, this causes the following error in the controller which shows that this attempt failed because the files were not assigned to the input element at all:




TypeError: 'undefined' is not an object (evaluating 'evt.target.files')




I already found out that the input.files property is read-only for security reasons. So I started another approach by dispatching a customized change which would provide the files property, but still without success.



So long story short: I'd be eager to learn a working solution or any best practices on how to approach this test case.


More From » angularjs

 Answers
140

Let's rethink AngularJS, DOM must be handled in a directive



We should not deal with DOM element in a controller, i.e. element.on('change', .., especially for testing purpose. In a controller, You talk to data, not to DOM.



Thus, those onchange should be a directive like the following



<input type=file name='file' ng-change=fileChanged() /> <br/>


However, unfortunately, ng-change does not work well with type=file. I am not sure that the future version works with this or not. We still can apply the same method though.



<input type=file 
onchange=angular.element(this).scope().fileChanged(this.files) />


and in the controller, we just simply define a method



$scope.fileChanged = function(files) {
return files.0.length < 500000;
};


Now, everything is just a normal controller test. No more dealing with angular.element, $compile, triggers, etc.! :)



describe(‘MyCtrl’, function() {
it('does check files', inject(
function($rootScope, $controller) {
scope = $rootScope.new();
ctrl = $controller(‘UploadCtrl’, {‘$scope’: scope});

var files = { 0: {name:'foo', size: 500001} };
expect(scope.fileChanged(files)).toBe(true);
}
));
});


http://plnkr.co/edit/1J7ETus0etBLO18FQDhK?p=preview


[#69940] Thursday, July 31, 2014, 10 Years  [reply] [flag answer]
Only authorized users can answer the question. Please sign in first, or register a free account.
zahrafrancisr

Total Points: 176
Total Questions: 105
Total Answers: 99

Location: Svalbard and Jan Mayen
Member since Sun, Sep 25, 2022
2 Years ago
;