300 lines
9.5 KiB
JavaScript

"use strict";
var express = require('express');
var fs = require('fs');
var mkdirp = require('mkdirp');
var middleware = require('../lib/middleware');
var os = require('os');
var request = require('supertest');
var assert = require('assert');
var copySync = require('fs-extra').copySync;
var path = require('path');
var tmpDest = __dirname + '/artifacts';
var clearCache = function(filename) {
if(fs.existsSync(tmpDest + '/' + filename)) {
// Unlinking file since it is cached without reguard to params.
// TODO: Remove when the imports cache is aware of params.
fs.unlinkSync(tmpDest + '/' + filename);
}
};
var setupExpress = function(src, options, staticDest) {
staticDest = staticDest || options.dest;
options = options || {};
var app = express();
app.use(middleware(src, options));
app.use(express.static(staticDest));
return app;
}
describe('middleware', function(){
describe('simple', function(){
var app = setupExpress(__dirname + '/fixtures', {
dest: tmpDest
});
it('should process simple less files', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/simple-exp.css', 'utf8');
request(app)
.get('/simple.css')
.expect(200)
.expect(expected, done);
});
});
describe('source map', function(){
var app = setupExpress(__dirname + '/fixtures', {
dest: tmpDest,
force: true, // Need to force since using the same file as the simple test.
render: {
sourceMap: {
sourceMapBasepath: __dirname + '/fixtures'
}
}
});
it('should handle source map files', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/simple-exp.css.map', 'utf8');
request(app)
.get('/simple.css.map')
.expect(200)
.expect(expected, done);
});
});
describe('import', function(){
var app = setupExpress(__dirname + '/fixtures', {
dest: tmpDest
});
it('should process less files with imports', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/importSimple-exp.css', 'utf8');
request(app)
.get('/importSimple.css')
.expect(200)
.expect(expected, done);
});
it('should process less files with nested imports', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/import-exp.css', 'utf8');
request(app)
.get('/import.css')
.expect(200)
.expect(expected, done);
});
});
describe('options', function(){
describe('postprocess', function(){
describe('css', function(){
var app = setupExpress(__dirname + '/fixtures', {
dest: tmpDest,
postprocess: {
css: function(css, req) {
return '/* Prepended Comment */\n' + css;
}
}
});
it('should prepend the comment on all output css', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/postprocessCss-exp.css', 'utf8');
request(app)
.get('/postprocessCss.css')
.expect(200)
.expect(expected, done);
});
});
});
describe('preprocess', function(){
describe('less', function(){
var app = setupExpress(__dirname + '/fixtures', {
dest: tmpDest,
preprocess: {
less: function(src, req) {
if (req.query.namespace) {
src = req.query.namespace + " { " + src + " }";
}
return src;
}
}
});
it('should add namespace when found', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/preprocessLess-exp-a.css', 'utf8');
clearCache('preprocessLess.css');
request(app)
.get('/preprocessLess.css?namespace=h1')
.expect(200)
.expect(expected, done);
});
it('should not add namespace when none provided', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/preprocessLess-exp-b.css', 'utf8');
clearCache('preprocessLess.css');
request(app)
.get('/preprocessLess.css')
.expect(200)
.expect(expected, done);
});
});
describe('path', function(){
var app = setupExpress(__dirname + '/fixtures', {
dest: tmpDest,
preprocess: {
path: function(pathname, req) {
return pathname.replace('.ltr', '');
}
}
});
it('should remove .ltr from the less path when found', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/preprocessPath-exp.css', 'utf8');
request(app)
.get('/preprocessPath.ltr.css')
.expect(200)
.expect(expected, done);
});
it('should not change less path when no matching .ltr', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/preprocessPath-exp.css', 'utf8');
request(app)
.get('/preprocessPath.css')
.expect(200)
.expect(expected, done);
});
});
describe('importPaths', function(){
var app = setupExpress(__dirname + '/fixtures', {
dest: tmpDest,
preprocess: {
path: function(pathname, req) {
var returnPath = pathname.replace(/(\/[0-9\.0-9\.0-9].*\/)/, '/');
return returnPath;
},
importPaths: function(paths, req) {
var version = req.url.match(/\/([0-9\.0-9\.0-9]*)/);
var reqPath = path.join(__dirname, 'fixtures', 'external', version[1] , '/');
var paths = [
reqPath,
path.join(reqPath, 'ui')
];
return paths;
}
}
});
it('should respond with newly mapped paths', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/preprocessParserPaths-exp.css', 'utf8');
request(app)
.get('/2.43.3/preprocessParserPaths.css')
.expect(200)
.expect(expected, done);
});
});
describe('pathRoot', function(){
var app = setupExpress('/fixtures', {
dest: '/artifacts',
pathRoot: __dirname
}, tmpDest);
it('should process simple less files', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/pathRoot-exp.css', 'utf8');
request(app)
.get('/pathRoot.css')
.expect(200)
.expect(expected, done);
});
});
});
});
describe('cacheFile', function() {
var middlewareSrc = tmpDest + '/fixturesCopy';
var dest = tmpDest + '/cacheFileTest';
var cacheFile = dest + '/cacheFile.json';
try {
mkdirp.sync(middlewareSrc);
} catch(e) {
if (e && e.code != 'EEXIST') throw e;
}
copySync(__dirname + '/fixtures', middlewareSrc);
var app;
var expandExpected = function(file) {
return file.replace(/\{\$\}/g, middlewareSrc);
}
var checkCacheFile = function(cacheFile, expectedFile){
var sortByPath = function(a, b) {
var keyA = a.path;
var keyB = b.path;
if(keyA < keyB) return -1;
if(keyA > keyB) return 1;
return 0;
};
return function(){
// Force cacheFile write.
middleware._saveCacheToFile();
var cacheFileExpected = JSON.parse(expandExpected(fs.readFileSync(expectedFile, 'utf8')));
var cacheFileOutput = JSON.parse(fs.readFileSync(cacheFile, 'utf8'));
for (var file in cacheFileExpected) {
assert(cacheFileOutput[file] != undefined);
var expectedImports = cacheFileExpected[file].imports.sort(sortByPath);
var outputImports = cacheFileOutput[file].imports.sort(sortByPath);
assert.equal(outputImports.length, expectedImports.length);
for (var i = 0; i < expectedImports.length; i++) {
assert.equal(expectedImports[i].path, outputImports[i].path);
}
}
}
}
beforeEach(function() {
// Unfortunately because cache-related items are stored in globals
// (which they need to be so that they are shared across different
// middleware invocations), to properly test the cacheFile option we
// need to re-require the middleware for each of these tests.
var mpath = path.resolve(__dirname, '../lib/middleware.js');
delete require.cache[mpath];
middleware = require('../lib/middleware');
app = setupExpress(middlewareSrc, {
dest: dest,
cacheFile: cacheFile
});
});
it('should process files correctly and store the right cached imports', function(done){
var expected = fs.readFileSync(__dirname + '/fixtures/import-exp.css', 'utf8');
request(app)
.get('/import.css')
.expect(200)
.expect(expected)
.expect(checkCacheFile(cacheFile, __dirname + '/fixtures/cacheFile-exp.json'))
.end(done);
});
it('should ignore cached imports if the file has changed and update cached imports', function(done){
copySync(middlewareSrc + '/importSimple.less', middlewareSrc + '/import.less');
var expected = fs.readFileSync(__dirname + '/fixtures/importSimple-exp.css', 'utf8');
request(app)
.get('/import.css')
.expect(200)
.expect(expected)
.expect(checkCacheFile(cacheFile, __dirname + '/fixtures/cacheFile-exp2.json'))
.end(done);
});
});
});