Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 19 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
# Karma-preprocessor-pullscripts: download Javascript from your project's html
# Karma-preprocessor-pullscripts: read Javascript referred to by your project's html

Tests usually require resources to be loaded, such as jquery and Angular. One can load these as files, but some apps don't keep local copies, and instead download scripts from CDNs, etc.
Instead of duplicating all your app's Javascript references in your Karma config, just refer to your HTML files, test files and mocks.

This preprocessor lets you load your project's HTML files, from which the files denoted by the `src` field in the `<script>` files are downloaded and included in the test suites.
Also supports reading external Javascript files via HTTP.

This preprocessor lets you load your project's HTML files, from which the files denoted by the `src` field in the `<script>` files are read or downloaded and included in the test suites.

This is handy for running your tests againts your dist package.

Pros:

1. You do not need to manage local copies of resources using, e.g. bower
2. Your project's html files are used, meaning you are guaranteed that there is consistency between test and live resources
1. Your project's html files are used, meaning you are guaranteed that there is consistency between test and live resources
2. You do not need to manage local copies of resources using, e.g. bower

Cons:
Cons for case 2:

1. Tests require Internet connectivity
2. Downloads on each test iteration
Expand All @@ -36,6 +40,15 @@ preprocessors: {
},
```

Configure the file prefix:
```js
pullscripts: {
// Prefix referenced local file names with this before reading them up.
filePrefix: 'dist/'
},
```


## Note on order

All scripts are processed in order of appearance in the HTML files. For example if `jquery` is included before `angular`, then the jquery script will be downloaded and included before the angular script. If you have multiple html files with different scripts included, you might need to ensure the load order (order in `files` array) is consistent.
Expand Down
7 changes: 4 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
var process = require('./parse');

var pullScripts = function (loggerFactory) {
var pullScripts = function (loggerFactory, config) {
var logger = loggerFactory.create('preprocessor:pullscripts');
config = typeof config === 'object' ? config : {};

return function (content, file, done) {
process(content, logger, done);
process(config, content, logger, done);
};
};

pullScripts.$inject = [ 'logger' ];
pullScripts.$inject = [ 'logger', 'config.pullscripts' ];

module.exports = {
'preprocessor:pullscripts': [ 'factory', pullScripts ]
Expand Down
45 changes: 35 additions & 10 deletions parse.js
Original file line number Diff line number Diff line change
@@ -1,39 +1,64 @@
var cheerio = require('cheerio'),
request = require('request'),
fs = require('fs');
async = require('async');

var process = module.exports = function (content, logger, callback) {
var process = module.exports = function (config, content, logger, callback) {
var html = cheerio.load(content),
urls = [];
urls = [],
filePrefix = config.filePrefix ? config.filePrefix: '';


html('script').each(function (index, obj) {
var src = obj.attribs.src,
url = null;
var src = obj.attribs.src;

if (src) {
if (src.match(/^http/)) {
urls.push(src);
} else if (src.match(/^\/\//)) {
urls.push('http:' + src);
}
} else {
urls.push(filePrefix + src);
}
}
});

async.map(urls, function (each, callback) {
request.get(each, function (err, resp, body) {
function getUrl(url, callback) {
request.get(url, function (err, resp, body) {
if (err || (resp && resp.statusCode !== 200)) {
err = err || resp.statusCode;
logger.error('Failed to download', each, err);
logger.error('Failed to download', url, err);
return callback(err);
}

callback(null, body);
});
}

function getFile(url, callback) {
fs.readFile(url, function(err, data) {
if (err) {
logger.error('Failed to read', url, err);
return callback(err);
}
callback(null, data);
});

}

async.map(urls, function (each, callback) {
logger.debug(each);
if (each.match(/^http/)) {
getUrl(each, callback);
} else {
getFile(each, callback);
}
}, function (err, content) {
if (err) {
return;
}

callback(content.join('\n'));
var s = content.join('\n');
callback(s);
});

};
55 changes: 39 additions & 16 deletions test.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,37 @@
var assert = require('assert');


describe('pullscripts', function () {
var request = require('request');
var fs = require('fs');

var parse = require('./parse');

var html = '<html>' +
'<head>' +
'<script src="http://firstUrl.com"></script>' +
'<script src="//secondUrl.com"></script>' +
'<script src="./ignoredLocalUrl"></script>' +
'<script src="./localFileIncluded.js"></script>' +
'</head>' +
'</html>';

var urls = [ 'http://firstUrl.com', 'http://secondUrl.com' ];
var urls = [ 'http://firstUrl.com', 'http://secondUrl.com', 'dist/./localFileIncluded.js' ];
var config = { filePrefix: 'dist/' };

var logger = {
error: function () { },
debug: function() {}
};

it('downloads eligible script files', function (done) {
request.get = function (url, callback) {
callback(null, { statusCode: 200 }, url);
};
fs.readFile = function(url, callback) {
callback(null, url);
};

parse(html, {}, function (content) {
parse(config, html, logger, function (content) {
assert.equal(content, urls.join('\n'));
done();
});
Expand All @@ -29,21 +41,32 @@ describe('pullscripts', function () {
request.get = function (url, callback) {
callback(null, { statusCode: 404 }, url);
};
fs.readFile = function(url, callback) {
callback('Fail', null);
};

var expectedErrors = urls.slice();

var logger = {
error: function () {
assert.equal(arguments[0], 'Failed to download');
assert.equal(arguments[1], expectedErrors.shift());
assert.equal(arguments[2], 404);
if (expectedErrors.length === 0) {
done();
}
}
};

parse(html, logger, function (content) {
var expectedErrors = urls.slice();

logger.error = function () {
var url = expectedErrors.shift();
if (url.match(/^http/)) {
assert.equal(arguments[0], 'Failed to download');
assert.equal(arguments[1], url);
assert.equal(arguments[2], 404);
} else {
assert.equal(arguments[0], 'Failed to read');
assert.equal(arguments[1], url);
assert.equal(arguments[2], 'Fail');
}

if (expectedErrors.length === 0) {
done();
}
}

parse(config, html, logger, function (content) {
assert(false, 'Shouldn\'t hit callback due to download errors');
});
});
Expand Down