gulp-css-spriter
Version:
Sprite Sheet Generation from CSS source files. The best and different approach to sprite sheets.
382 lines (298 loc) • 11.6 kB
JavaScript
var Promise = require('bluebird');
var chai = require('chai');
var expect = require('chai').expect;
var chaiAsPromised = require('chai-as-promised');
chai.use(chaiAsPromised);
var extend = require('extend');
var path = require('path');
var fs = require('fs');
var readFile = Promise.promisify(fs.readFile);
var gutil = require('gulp-util');
var css = require('css');
// The main gulp plugin to test
var spriter = require('../');
// Test out some individual components
var getBackgroundImageDeclarations = require('../lib/get-background-image-declarations');
var transformMap = require('../lib/transform-map');
// We use 'algorithm': 'top-down' because it easier to have a consistent packing so the expected doesn't have to be updated
describe('gulp-css-spriter', function() {
it('should emit a buffer', function() {
var spriterPromise = spriterTest({}).then(function(result) {
return result.isBuffer();
});
return expect(spriterPromise).to.eventually.equal(true);
});
it('should work with minified css', function() {
return compareSpriterResultsToExpected('test/test-css/background.min.css', 'test/test-css/expected/background.min.css', {
'spritesmithOptions': {
'algorithm': 'top-down'
}
});
});
it('should not try to sprite external images', function() {
return compareSpriterResultsToExpected('test/test-css/external-image.css', 'test/test-css/expected/external-image.css', {
'spritesmithOptions': {
'algorithm': 'top-down'
}
});
});
it('should sprite properly when the same image source is used in multiple declarations. And one of the declarations is excluded via meta data', function() {
return compareSpriterResultsToExpected('test/test-css/multiple-declarations-same-image.css', 'test/test-css/expected/multiple-declarations-same-image.css', {
'spritesmithOptions': {
'algorithm': 'top-down'
}
});
});
// Declarations in keyframes will be sprited
it('should work with css animation keyframes', function() {
return compareSpriterResultsToExpected('test/test-css/keyframes.css', 'test/test-css/expected/keyframes.css', {
'spritesmithOptions': {
'algorithm': 'top-down'
}
});
});
// All declarations will be included except those with explcit `includeMode` false meta data
it('should work in implicit mode `options.includeMode`', function() {
return compareSpriterResultsToExpected('test/test-css/overall.css', 'test/test-css/expected/overall-include-implicit.css', {
'includeMode': 'implicit',
'spritesmithOptions': {
'algorithm': 'top-down'
}
});
});
// Only declarations with explicit `includeMode` true meta data, will be sprited
it('should work in explicit mode `options.includeMode`', function() {
return compareSpriterResultsToExpected('test/test-css/overall.css', 'test/test-css/expected/overall-include-explicit.css', {
'includeMode': 'explicit',
'spritesmithOptions': {
'algorithm': 'top-down'
}
});
});
it('should throw error with non-existent file when `options.silent` is false', function() {
var spriterPromise = spriterTest({
'silent': false
}, 'test/test-css/non-existent-image.css');
return expect(spriterPromise).to.eventually.be.rejected;
});
it('should verify images `options.shouldVerifyImagesExist`', function() {
// This should throw
var spriterPromiseNoCheck = spriterTest({
'shouldVerifyImagesExist': false
}, 'test/test-css/non-existent-image.css');
// This should pass because we verify first
var spriterPromiseCheck = spriterTest({
'shouldVerifyImagesExist': true
}, 'test/test-css/non-existent-image.css');
return Promise.all([
expect(spriterPromiseNoCheck).to.eventually.be.rejected,
expect(spriterPromiseCheck).to.eventually.be.fulfilled
]);
});
it('should call `includeMode.spriteSheetBuildCallback` when done', function() {
return spriteSheetBuildCallbackResultTest({}).then(function(result) {
return Promise.all([
expect(result).to.have.property('image'),
expect(result).to.have.property('coordinates'),
expect(result).to.have.property('properties')
]);
});
});
it('should pass options through to spritesmith using `options.spritesmithOptions`', function() {
// We make sure the spritesmith options were passed by using opposite-style stacking algorithms
// and then comparing the width/height of both
var testDifferentStackingPromise = Promise.all([
buildCallbackWithAlgorithmPromise('top-down'),
buildCallbackWithAlgorithmPromise('left-right')
]);
return testDifferentStackingPromise.then(function(res) {
var verticalStackingData = res[0];
var horizontalStackingData = res[1];
// Make sure the two proportions are different
return Promise.all([
expect(verticalStackingData.properties.height).to.be.above(horizontalStackingData.properties.height),
expect(horizontalStackingData.properties.width).to.be.above(verticalStackingData.properties.width)
]);
});
function buildCallbackWithAlgorithmPromise(algorithm) {
var extraSpriterOps = {
spritesmithOptions: {
algorithm: algorithm
}
};
return spriteSheetBuildCallbackResultTest(extraSpriterOps);
}
});
// Get a promise that resolves with the transformed file/chunks
function spriterTest(spriterOptions, filePath) {
spriterOptions = spriterOptions || {};
filePath = filePath || 'test/test-css/overall.css';
var whenSpriterDonePromise = new Promise(function(resolve, reject) {
readFile(filePath).then(function(contents) {
contents = String(contents);
// create the fake file
var fakeFile = new gutil.File({
base: process.cwd(),
cwd: process.cwd(),
path: path.join(process.cwd(), filePath),
contents: new Buffer(contents)
});
// Create a spriter plugin stream
var mySpriter = spriter(spriterOptions);
// wait for the file to come back out
mySpriter.on('data', function(file) {
resolve(file);
});
mySpriter.on('error', function(err) {
reject(err);
});
mySpriter.on('end', function() {
resolve();
});
// write the fake file to it
mySpriter.write(fakeFile);
mySpriter.end();
}, function(err) {
reject(err);
});
});
return whenSpriterDonePromise;
}
// Get a promise representing the result of `options.spriteSheetBuildCallback`
function spriteSheetBuildCallbackResultTest(opts, filePath) {
return new Promise(function(resolve, reject) {
var spriterOpts = extend({}, {
spriteSheetBuildCallback: function(err, result) {
if(err) {
reject(err);
}
else {
resolve(result);
}
}
}, opts);
spriterTest(spriterOpts, filePath).then(function(file) {
// nothing
}, function(err) {
reject(err);
});
});
}
function compareSpriterResultsToExpected(actualPath, expectedPath, options) {
options = options || {};
var spriterPromise = spriterTest(options, actualPath).then(function(result) {
console.log(String(result.contents));
return String(result.contents);
});
return readFile(expectedPath).then(function(expectedResult) {
return expect(spriterPromise).to.eventually.equal(String(expectedResult));
});
}
});
describe('lib/getBackgroundImageDeclarations(...)', function() {
it('should work with single background declarations', function() {
return testGetBackgroundImageDeclarationsFromFile('test/test-css/background.css', 2);
});
it('should work with single background-image declarations', function() {
return testGetBackgroundImageDeclarationsFromFile('test/test-css/background-image.css', 1);
});
it('should work with mulitple images defined in background(-image) declarations', function() {
return testGetBackgroundImageDeclarationsFromFile('test/test-css/multiple-backgrounds.css', 2);
});
it('should factor in the `include` meta data', function() {
return testGetBackgroundImageDeclarationsFromFile('test/test-css/meta-include.css', 1);
});
it('should work with minified css', function() {
return testGetBackgroundImageDeclarationsFromFile('test/test-css/background.min.css', 2);
});
it('should work with single background declarations in keyframes', function() {
return testGetBackgroundImageDeclarationsFromFile('test/test-css/keyframes.css', 4);
});
function testGetBackgroundImageDeclarationsFromFile(filePath, numExpectedDeclarations) {
return readFile(filePath).then(function(contents) {
contents = String(contents);
var styles = css.parse(contents, {
'silent': false
});
var imageDeclarations = getBackgroundImageDeclarations(styles);
return expect((imageDeclarations || []).length).to.equal(numExpectedDeclarations);
});
}
});
describe('lib/transformMap(...)', function() {
var testArray;
beforeEach(function() {
testArray = [1, 2, 3];
});
it('should transform value with bare value', function() {
var result = transformMap(testArray, function(el) {
if(el == 2) {
return 2.1;
}
});
expect(result).to.deep.equal([1, 2.1, 3]);
});
it('should transform value with value property', function() {
var result = transformMap(testArray, function(el) {
if(el == 2) {
return {
value: 2.1
};
}
});
expect(result).to.deep.equal([1, 2.1, 3]);
});
it('should insert with bare value', function() {
var result = transformMap(testArray, function(el) {
if(el == 2) {
return {
insertElements: 2.5
};
}
});
expect(result).to.deep.equal([1, 2, 2.5, 3]);
});
it('should insert with array', function() {
var result = transformMap(testArray, function(el) {
if(el == 2) {
return {
insertElements: [2.5, 2.8]
};
}
});
expect(result).to.deep.equal([1, 2, 2.5, 2.8, 3]);
});
it('should append bare value', function() {
var result = transformMap(testArray, function(el) {
if(el == 2) {
return {
appendElements: 4
};
}
});
expect(result).to.deep.equal([1, 2, 3, 4]);
});
it('should append with array', function() {
var result = transformMap(testArray, function(el) {
if(el == 2) {
return {
appendElements: [4, 5]
};
}
});
expect(result).to.deep.equal([1, 2, 3, 4, 5]);
});
it('should iterate over the "extra" added elements', function() {
var iterateResult = [];
var result = transformMap(testArray, function(el) {
iterateResult.push(el);
if(el == 2) {
return {
insertElements: [2.5, 2.8],
appendElements: [4, 5]
};
}
});
expect(iterateResult).to.deep.equal([1, 2, 2.5, 2.8, 3, 4, 5]);
});
});