How to load binary file during javascript unit test?

In my application, the user uses HTML5 drag and drop to process the binary. This part of the code is working fine. In chrome, I drag and drop the binary and use FileReader to create an arrayBuffer. Everything seems to be working fine. I am writing tests for this functionality, and I am at a loss. How to load a binary file into my unit test? For the bit of code I'm testing, I only need arrayBuffer. I am currently creating an arrayBuffer manually, but this is not a sustainable solution. In order for my tests to be effective, I need to be able to run a new binary at any time and do a new test. My test environment is pasty + jasmine.

( function() {"use strict"; function loadSimpleDataView() { //instead of defining ArrayBuffer, //I want it to be generated based upon an external file var buffer = new ArrayBuffer(4), dataView = new DataView(buffer), int8View = new Int8Array(buffer); int8View.set([0x00,0x01,0x02,0x03]); return dataView; } describe('mymodule', function() { it('mytest', function() { var dataView = loadSimpleDataView(); expect(dataView).toBeDefined(); //do rest of tests }); }); }()); 
+4
source share
3 answers

I use grunt to create my project and run my unit tests. Below are my grunt.js , testacular.conf.js and test ( javaclassstreamreader.spec.js ). In short, grunt runs grunt-server , which is configured to serve binary data files. Testacular is configured on the grunt-server proxy. In the test, XMLHttpRequest used to extract a binary file. Everything works, but it seems a bit complicated. Is there an easier way?

grunt.js:

 /*global module:false*/ module.exports = function(grunt) {"use strict"; // Project configuration. grunt.initConfig({ pkg : '<json:package.json>', meta : { banner : '/*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - ' + '<%= grunt.template.today("yyyy-mm-dd") %>\n' + '<%= pkg.homepage ? "* " + pkg.homepage + "\n" : "" %>' + '* Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author.name %>;' + ' Licensed <%= _.pluck(pkg.licenses, "type").join(", ") %> */' }, lint : { files : ['grunt.js', 'src/*.js', 'src/public/js/**/*.js', 'src/specs/**/*.js'] }, watch : { files : '<config:lint.files>', tasks : 'default' }, exec : { ensure_generated_directory : { command : 'mkdir -p generated/js/' } }, clean : { all : ['generated'] }, jshint : { files : '<config:lint.files>', options : { curly : true, eqeqeq : true, forin : true, immed : true, latedef : true, newcap : true, noarg : true, sub : true, undef : true, unused : true, strict : true, boss : true, eqnull : true, es5 : true, browser : true, jquery : true, devel : true }, globals : { //jasmine describe : false, it : false, expect : false, //commonjs require : false, exports : true, //angular angular : false } }, 'closure-compiler' : { frontend : { closurePath : 'closure-compiler', js : ['src/*.js', 'src/public/js/**/*.js'], jsOutputFile : 'generated/js/complete-app.js', options : { externs : 'externs.js', compilation_level : 'SIMPLE_OPTIMIZATIONS', language_in : 'ECMASCRIPT5_STRICT', logging_level : 'ALL', debug : null, warning_level : 'verbose', summary_detail_level : 3, formatting : ['PRETTY_PRINT', 'PRINT_INPUT_DELIMITER'], common_js_entry_module : 'src/public/js/app.js', process_common_js_modules : null, process_jquery_primitives : null, common_js_module_path_prefix : 'src' } } }, testacularServer : { integration : { options : { keepalive : true }, configFile : 'testacular.conf.js', autoWatch : false, singleRun : true } }, server : { port : 18081, base : './src/specs/data' } }); // Default task. grunt.registerTask('default', 'lint exec:ensure_generated_directory closure-compiler server testacularServer:integration'); grunt.loadNpmTasks('grunt-contrib-watch'); grunt.loadNpmTasks('grunt-closure-compiler'); grunt.loadNpmTasks('grunt-exec'); grunt.loadNpmTasks('grunt-contrib-clean'); grunt.loadNpmTasks('grunt-testacular'); }; 

testacular.conf.js:

 // Testacular configuration // Generated on Tue Jan 01 2013 03:17:01 GMT-0500 (EST) /*global basePath:true */ /*global files:true */ /*global JASMINE:false */ /*global JASMINE_ADAPTER:false */ /*global exclude:true */ /*global reporters:true */ /*global port:true */ /*global runnerPort:true */ /*global colors:true */ /*global logLevel:true */ /*global LOG_INFO:false */ /*global autoWatch:true */ /*global browsers:true */ /*global captureTimeout:true */ /*global singleRun:true */ // base path, that will be used to resolve files and exclude basePath = '.'; // list of files / patterns to load in the browser files = [ JASMINE, JASMINE_ADAPTER, 'src/public/lib/jquery/1.7.2/jquery-1.7.2.min.js', 'src/public/lib/jquery-ui/1.8.20.custom/jquery-ui-1.8.20.custom.min.js', 'src/public/lib/angular/1.0.1/angular-1.0.1.min.js', 'src/public/lib/filer.min.js', 'generated/js/complete-app.js', 'src/specs/**/*.spec.js' ]; // list of files to exclude exclude = [ ]; // test results reporter to use // possible values: 'dots', 'progress', 'junit' reporters = ['progress']; // web server port port = 18080; // cli runner port runnerPort = 9100; //proxy proxies = { '/test-data/': 'http://localhost:18081/' }; // enable / disable colors in the output (reporters and logs) colors = true; // level of logging // possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG logLevel = LOG_INFO; // enable / disable watching file and executing tests whenever any file changes autoWatch = true; // Start these browsers, currently available: // - Chrome // - ChromeCanary // - Firefox // - Opera // - Safari (only Mac) // - PhantomJS // - IE (only Windows) browsers = ['Chrome']; // If browser does not capture in given timeout [ms], kill it captureTimeout = 5000; // Continuous Integration mode // if true, it capture browsers, run tests and exit singleRun = false; 

javaclassstreamreader.spec.js:

 /*global module$javaclassstreamreader:false */ /*global waitsFor:false */ /*global runs:false */ /*global dump:false */ ( function() {"use strict"; var JavaClassStreamReader = module$javaclassstreamreader.JavaClassStreamReader; function loadSimpleDataView(callback) { var dataView; var xhr = new XMLHttpRequest(); xhr.open('GET', '/test-data/MyInterface.class', true); xhr.responseType = 'arraybuffer'; xhr.onload = function() { dataView = new DataView(this.response); callback(dataView); }; xhr.onerror = dump; xhr.send(); } describe('javaclassstreamreader', function() { it('reader can be constructed', function() { var hasData = false,reader; loadSimpleDataView(function(dataView) { reader = new JavaClassStreamReader(dataView); hasData = true; }); waitsFor(function() { return hasData; }, "Never retrieved file", 3000); runs(function() { expect(reader.offset).toBe(0); var firstBytes = reader.getU4(); dump(firstBytes.toString(16)); expect(firstBytes).toBe(0xcafebabe); expect(reader.maxOffset).toBe(126); }); }); }); }()); 
+2
source

I solved the problem by encoding the binary as a hexadecimal string, filling it with a blob and calling a function that takes a File object. I use Jasmine to test file upload function. FileLoader is a class that I wrote that has a loadFromFile function.

  beforeEach(function() { return this.fileLoader = new FileLoader() }); it("can load this bin file safely", function() { var blob, fileContentsEncodedInHex; fileContentsEncodedInHex = ["\x45\x6e\x63\x6f\x64\x65\x49\x6e\x48\x65\x78\x42\x65\x63\x61\x75\x73\x65\x42\x69\x6e\x61\x72\x79\x46\x69\x6c\x65\x73\x43\x6f\x6e\x74\x61\x69\x6e\x55\x6e\x70\x72\x69\x6e\x74\x61\x62\x6c\x65\x43\x68\x61\x72\x61\x63\x74\x65\x72\x73"]; blob = new Blob(fileContentsEncodedInHex); this.fileLoader.loadFromFile(blob); }); 

The function in the file loader class looks something like this (in coffeescript). You must add error handling to your sales code ...

  # # Load the file # # @param [File] file # The DOM file object # loadFromFile: (file) -> # create the file reader # assign the file load handler # read the array as a buffer, which will trigger the handler reader = new FileReader() reader.onloadend = this._handleOnLoadEnd reader.readAsArrayBuffer(file) return 

I wrote the following small python application to output the contents of a file as a hex encoded string that can be used as the contents of a javascript string. (FYI, this is my first python script after about 12 years, I'm sure it is inefficient, but it was fast) Many thanks to a colleague for the output line. I'm not sure why the code block gets confused on the display. My apologies.

 import sys fulldoc = "" with open('your file', 'rb') as readFile: while 1: character = readFile.read(1) if not character: break fulldoc = fulldoc + "\\x" + character.encode("hex") print fulldoc 

Extra tidbits ... I don't know your specific situation, but I would suggest the following ...

If you need to add a new binary at any time and re-run the tests, then you must do a test for each kind of binary that you arbitrarily add. Check both positive and negative files (i.e. Files that should work with your application, files that should not work with your application). There are unit test frameworks for this. In the future, if you find that the file that your application crashes due to an error in your code, you can fix the error, add a new unit test to load this binary, and then the tests should pass. If at some point you happen to break the file processing code during an accident, unit tests are always present to show that something is wrong.

+5
source

I think you can run the additional grunt server on a different port to serve the binary. In the latest version of karma, you can define some details about how files are included and maintained by the karma server. That way, you would include your test data in the files and tell Karma to serve, but not watch or include these files.

 files = [ JASMINE, JASMINE_ADAPTER, 'src/public/lib/jquery/1.7.2/jquery-1.7.2.min.js', 'src/public/lib/jquery-ui/1.8.20.custom/jquery-ui-1.8.20.custom.min.js', 'src/public/lib/angular/1.0.1/angular-1.0.1.min.js', 'src/public/lib/filer.min.js', /* NEW LINE NEEDED BELOW! */ {pattern: 'src/specs/data/**', watched: false, included: false, served: true}, 'generated/js/complete-app.js', 'src/specs/**/*.spec.js' ]; 

Then in your test you need to get the correct file location in the xhr.open method. If you pass an empty string to xhr.open ('GET', '') and unload responseText, you will get output with all the included scripts / files transferred to Karma, and there you will find paths starting with '/ base /', and sometimes '/ absolute /'. Not sure if Karma does 100%, but I tried using '/ base /' as a prefix for the xhr.open path and it seems to work :)

  var url = '/base/src/specs/data/MyInterface.class'; xhr.open('GET', url, true); 

I am also working on a project that includes reading binary data files on the client, as well as working with karma for testing, so it was great to read your question for inspiration. I decided to find new functionality in Karma, which is really useful for simplifying the approach!

+2
source

All Articles