plumber-requirejs
Version:
RequireJS (r.js) operation for Plumber pipelines
342 lines (281 loc) • 13.7 kB
JavaScript
var chai = require('chai');
chai.should();
var sinon = require("sinon");
var sinonChai = require("sinon-chai");
chai.use(sinonChai);
var SourceMapConsumer = require('source-map').SourceMapConsumer;
var fs = require('fs');
var runOperation = require('plumber-util-test').runOperation;
var completeWithResources = require('plumber-util-test').completeWithResources;
var Resource = require('plumber').Resource;
var Supervisor = require('plumber/lib/util/supervisor');
var SourceMap = require('mercator').SourceMap;
var requirejs = require('..');
function createResource(params) {
return new Resource(params);
}
function resourcesError() {
chai.assert(false, "error in resources observable");
}
describe('requirejs', function(){
// var supervisor;
// beforeEach(function() {
// supervisor = new Supervisor();
// supervisor.dependOn = sinon.spy();
// });
it('should be a function', function(){
requirejs.should.be.a('function');
});
it('should return a function', function(){
requirejs().should.be.a('function');
});
it('should throw an error if passed an illegal option', function(){
(function() {
requirejs({name: 'foo'});
}).should.throw("'name' option should not be used with plumber-requirejs, see documentation");
(function() {
requirejs({out: 'foo'});
}).should.throw("'out' option should not be used with plumber-requirejs, see documentation");
(function() {
requirejs({baseUrl: 'foo'});
}).should.throw("'baseUrl' option should not be used with plumber-requirejs, see documentation");
});
it('should throw an exception if passed a directory', function(){
(function() {
runOperation(requirejs(), [createResource({path: 'test/fixtures'})]).resources.toArray();
}).should.throw('RequireJS does not support optimising directories yet');
});
describe('when passed a single AMD module', function() {
var transformedResources;
beforeEach(function() {
var resource = createResource({path: 'test/fixtures/app.js', type: 'javascript'});
transformedResources = runOperation(requirejs(), [resource]).resources;
});
it('should return a resource with the same path and filename', function(done){
completeWithResources(transformedResources, function(resources) {
resources.length.should.equal(1);
resources[0].path().absolute().should.equal('test/fixtures/app.js');
resources[0].filename().should.equal('app.js');
}, resourcesError, done);
});
it('should return the same data, with explicit name and dependencies', function(done){
completeWithResources(transformedResources, function(resources) {
resources[0].data().should.equal("define('app',[],function() {\n return 42;\n});\n\n");
}, resourcesError, done);
});
it('should return it with an identity sourcemap', function(done){
completeWithResources(transformedResources, function(resources) {
var map = new SourceMapConsumer(resources[0].sourceMap());
map.sources.should.deep.equal(['test/fixtures/app.js']);
map.sourcesContent.should.deep.equal(["define('app',[],function() {\n return 42;\n});\n\n"]);
// identity mapping
for (var i = 1; i <= 5; i++) {
map.originalPositionFor({line: i, column: 0}).should.deep.equal({
source: 'test/fixtures/app.js',
line: i,
column: 0,
name: null
});
}
}, resourcesError, done);
});
});
describe('when passed an AMD module with a dependency', function() {
var transformedResources;
beforeEach(function() {
var resource = createResource({path: 'test/fixtures/multi.js', type: 'javascript'});
transformedResources = runOperation(requirejs(), [resource]).resources;
});
it('should return a resource with the same path and filename', function(done){
completeWithResources(transformedResources, function(resources) {
resources.length.should.equal(1);
resources[0].path().absolute().should.equal('test/fixtures/multi.js');
resources[0].filename().should.equal('multi.js');
}, resourcesError, done);
});
it('should return a resource containing the dependency', function(done){
completeWithResources(transformedResources, function(resources) {
resources[0].data().should.equal("define('other',[],function() {\n return 100;\n});\n\ndefine('multi',['other'], function(other) {\n return other + 1;\n});\n\n");
}, resourcesError, done);
});
it('should return it with a sourcemap', function(done){
completeWithResources(transformedResources, function(resources) {
var map = new SourceMapConsumer(resources[0].sourceMap());
map.sources.should.deep.equal(['test/fixtures/other.js', 'test/fixtures/multi.js']);
map.sourcesContent.should.deep.equal([
"define('other',[],function() {\n return 100;\n});\n\n",
"define('multi',['other'], function(other) {\n return other + 1;\n});\n\n"
]);
/*
1 define('other',[],function() {
2 return 100;
3 });
4
5 define('multi',['other'], function(other) {
6 return other + 1;
7 });
*/
// first file has identity mapping
var i, offset = 5;
for (i = 1; i < offset; i++) {
map.originalPositionFor({line: i, column: 0}).should.deep.equal({
source: 'test/fixtures/other.js',
line: i,
column: 0,
name: null
});
}
// second file is offset
for (i = 0; i < 3; i++) {
map.originalPositionFor({line: i + offset, column: 0}).should.deep.equal({
source: 'test/fixtures/multi.js',
line: i + 1, // numbering from 1
column: 0,
name: null
});
}
}, resourcesError, done);
});
// FIXME: must re-introduce supervisor in plumber 0.3?
it.skip('should notify the supervisor of the dependency', function(done){
completeWithResources(transformedResources, function(resources) {
supervisor.dependOn.should.have.callCount(1);
supervisor.dependOn.should.have.been.calledWith(['test/fixtures/other.js']);
}, resourcesError, done);
});
});
describe('when passed a non-AMD file', function() {
var transformedResources;
beforeEach(function() {
var nonAmdResource = createResource({path: 'test/fixtures/not-amd.js', type: 'javascript'});
transformedResources = runOperation(requirejs(), [nonAmdResource]).resources;
});
it('should return a resource with the same path and filename', function(done){
completeWithResources(transformedResources, function(resources) {
resources.length.should.equal(1);
resources[0].path().absolute().should.equal('test/fixtures/not-amd.js');
resources[0].filename().should.equal('not-amd.js');
}, resourcesError, done);
});
it('should return the same data as was input', function(done){
completeWithResources(transformedResources, function(resources) {
resources[0].data().should.equal("var thisFileIsNotAnAMDModule = true;\n\nfunction meh() {}\n;\ndefine(\"not-amd\", function(){});\n\n");
}, resourcesError, done);
});
it('should return it with an identity sourcemap', function(done){
completeWithResources(transformedResources, function(resources) {
var map = new SourceMapConsumer(resources[0].sourceMap());
map.sources.should.deep.equal(['test/fixtures/not-amd.js']);
map.sourcesContent.should.deep.equal(["var thisFileIsNotAnAMDModule = true;\n\nfunction meh() {}\n;\ndefine(\"not-amd\", function(){});\n\n"]);
// identity mapping
for (var i = 1; i <= 5; i++) {
map.originalPositionFor({line: i, column: 0}).should.deep.equal({
source: 'test/fixtures/not-amd.js',
line: i,
column: 0,
name: null
});
}
}, resourcesError, done);
});
});
describe('when passed two AMD files', function() {
var transformedResources;
beforeEach(function() {
transformedResources = runOperation(requirejs(), [
createResource({path: 'test/fixtures/app.js', type: 'javascript'}),
createResource({path: 'test/fixtures/multi.js', type: 'javascript'})
]).resources;
});
it.only('should return two resources', function(done){
completeWithResources(transformedResources, function(resources) {
resources.length.should.equal(2);
}, resourcesError, done);
});
});
describe('when passed a file with a source map', function() {
var transformedResources;
var mainData = fs.readFileSync('test/fixtures/concatenated.js').toString();
var mainMapData = SourceMap.fromMapData(fs.readFileSync('test/fixtures/concatenated.js.map').toString());
beforeEach(function() {
transformedResources = runOperation(requirejs(), [
createResource({path: 'test/fixtures/concatenated.js', type: 'javascript',
data: mainData, sourceMap: mainMapData})
]).resources;
});
// FIXME: failing test because it seems to pick up the source
// map url comment at the end and map it to the concatenated
// file, which ends up in `sources' (it should not).
// As per the comment in the test below, it'd be
// better to have a more realistic test, e.g. a
// coffeescript AMD file that has real deps.
it.skip('should return a resource with a source map with correct properties from the input source map', function(done){
completeWithResources(transformedResources, function(resources) {
var sourceMap = resources[0].sourceMap();
sourceMap.file.should.equal('concatenated.js');
sourceMap.sources.should.deep.equal(mainMapData.sources);
sourceMap.sourcesContent.should.deep.equal(mainMapData.sourcesContent);
}, resourcesError, done);
});
it('should remap mappings based on the input source map', function(done) {
completeWithResources(transformedResources, function(resources) {
var map = new SourceMapConsumer(resources[0].sourceMap());
// FIXME: a better test would make requirejs do
// something, rather than these non-AMD files that are
// probably just passed through
/*
1 var x = 3;
2 var y = x;
3
4 /\* a comment *\/
5
6 function inc(x) {
7 return x + 1;
8 }
9
10 var z = inc(x);
11
12
13 /\* non-sensical library *\/
14 define('concatenated',[], function() {
15 var number = 1;
16
17 function addNumber(n) {
18 return n + number;
19 }
20
21 return addNumber;
22 });
*/
map.originalPositionFor({line: 1, column: 0}).should.deep.equal({
source: 'test/fixtures/source.js',
line: 1,
column: 0,
name: null
});
}, resourcesError, done);
});
// FIXME: must re-introduce supervisor in plumber 0.3?
it.skip('should register no path in the supervisor', function(done){
completeWithResources(transformedResources, function() {
supervisor.dependOn.should.not.have.been.called;
}, resourcesError, done);
});
});
/* TODO: must manually load requirejs-text plugin
describe('when passed an AMD file with a text! dependency', function() {
var transformedResources;
beforeEach(function() {
transformedResources = requirejs()([
createResource({path: 'test/fixtures/with-text.js', type: 'javascript'})
]);
});
it('should correctly reference the text dependency', function(){
return transformedResources.then(function(resources) {
resources.length.should.equal(2);
});
});
});
*/
// TODO: check if rjs error
});