specs/lib/index.spec.js
/* eslint-env mocha */
import { build, Builder, Node } from 'broccoli-fixture';
import { default as chai, expect } from 'chai';
import chaiAsPromised from 'chai-as-promised';
import Promise from 'bluebird';
import { spy } from 'sinon';
import { readSync, writeSync } from 'fixturify';
import BroccoliSass from '../../src/lib';
chai.use(chaiAsPromised);
function wait(ms) {
return (result) => new Promise(resolve => {
setTimeout(() => {
resolve(result);
}, ms);
});
}
describe('broccoli-sass-dir', () => {
it('compiles .scss files', () => {
const inputNode = new Node({
'app.scss': 'html { body { font: Helvetica; } }',
});
const sass = new BroccoliSass([inputNode]);
return expect(build(sass)).to.eventually.deep.equal({
'app.css': 'html body {\n font: Helvetica; }\n',
});
});
it('resolves @import statements', () => {
const inputNode = new Node({
'app1.scss': 'html { body { font: Helvetica; } }',
'app2.scss': '@import "app1";',
});
const sass = new BroccoliSass([inputNode]);
return expect(build(sass)).to.eventually.deep.equal({
'app1.css': 'html body {\n font: Helvetica; }\n',
'app2.css': 'html body {\n font: Helvetica; }\n',
});
});
it('does not render templates', () => {
const inputNode = new Node({
'my_app.scss': '@import "template";',
'_template.scss': 'html { body { font: Helvetica; } }',
});
const sass = new BroccoliSass([inputNode]);
return expect(build(sass)).to.eventually.deep.equal({
'my_app.css': 'html body {\n font: Helvetica; }\n',
});
});
it('preserves directory structure', () => {
const inputNode = new Node({
'app1.scss': 'html { body { font: Helvetica; } }',
subdir: {
'app2.scss': '@import "../app1";',
},
});
const sass = new BroccoliSass([inputNode]);
return expect(build(sass)).to.eventually.deep.equal({
'app1.css': 'html body {\n font: Helvetica; }\n',
subdir: {
'app2.css': 'html body {\n font: Helvetica; }\n',
},
});
});
it('throws an error on Syntax error', () => {
const inputNode = new Node({
'app.scss': 'html { body { font: Helvetica; } ]',
});
const sass = new BroccoliSass([inputNode]);
return expect(build(sass)).to.eventually.be.rejectedWith(
Error, 'Invalid CSS after "...t: Helvetica; }": expected "{", was "]"'
);
});
it('generates .map sourcemaps', () => {
const inputNode = new Node({
'app.scss': 'html { body { font: Helvetica; } }',
});
const sass = new BroccoliSass([inputNode], {
sassOptions: {
sourceMap: true,
},
});
return expect(build(sass)).to.eventually
.have.property('app.map')
.that.match(/\/app\.scss"/)
.that.match(/"version": 3,/)
.that.match(/"file": "app\.css",/)
.that.match(/"sources": /)
.and.match(/"mappings": /);
});
it('generates inline sourcemaps', () => {
const inputNode = new Node({
'app.scss': 'html { body { font: Helvetica; } }',
});
const sass = new BroccoliSass([inputNode], {
sassOptions: {
sourceMap: true,
sourceMapEmbed: true,
},
});
const result = build(sass);
return Promise.all([
expect(result).to.eventually.not.have.property('app.map'),
expect(result).to.eventually
.have.property('app.css')
.that.match(/ sourceMappingURL=data:application\/json;base64,\w+ /),
]);
});
it('supports including the contents in the source maps information', () => {
const inputNode = new Node({
'app.scss': 'html { body { font: Helvetica; } }',
});
const sass = new BroccoliSass([inputNode], {
sassOptions: {
sourceMap: true,
sourceMapContents: true,
},
});
const result = build(sass);
return Promise.all([
expect(result).to.eventually
.have.property('app.map')
.that.not.match(/"app\.scss"/),
expect(result).to.eventually
.have.property('app.map')
.that.match(/"sourcesContent": /)
.and.match(/html { body { font: Helvetica; } }/),
]);
});
it('supports defining the source maps root', () => {
const inputNode = new Node({
'app.scss': 'html { body { font: Helvetica; } }',
});
const sass = new BroccoliSass([inputNode], {
sassOptions: {
sourceMap: true,
sourceMapRoot: '/src/scss',
},
});
return expect(build(sass)).to.eventually
.have.property('app.map')
.that.match(/"sourceRoot": "\/src\/scss"/);
});
it('rebuilds correctly', () => {
const input = {
'app1.scss': 'html { body { font: Helvetica; } }',
'app2.scss': 'html { body { font: serif; } }',
subdir: {
'app3.scss': '@import "../app1";',
'app4.scss': '@import "../app2";',
},
};
const inputNode = new Node(input);
const sass = new BroccoliSass([inputNode]);
const fixtureBuilder = new Builder(sass);
return fixtureBuilder.build()
.then(wait(1000))
.then(() => {
spy(sass, 'render');
writeSync(inputNode.outputPath, {
'app1.scss': 'html { body { font: sans-serif; } }',
});
return readSync(inputNode.outputPath);
})
.then(() => sass.build())
.then(() => {
expect(readSync(sass.outputPath)).to.deep.equal({
'app1.css': 'html body {\n font: sans-serif; }\n',
'app2.css': 'html body {\n font: serif; }\n',
subdir: {
'app3.css': 'html body {\n font: sans-serif; }\n',
'app4.css': 'html body {\n font: serif; }\n',
},
});
});
});
it('rebuilds only modified files', () => {
const input = {
'app1.scss': 'html { body { font: Helvetica; } }',
'app2.scss': 'html { body { font: serif; } }',
subdir: {
'app3.scss': '@import "../app1";',
'app4.scss': '@import "../app2";',
},
};
const inputNode = new Node(input);
const sass = new BroccoliSass([inputNode]);
const fixtureBuilder = new Builder(sass);
return fixtureBuilder.build()
.then(wait(1000))
.then(() => {
spy(sass, 'render');
writeSync(inputNode.outputPath, {
'app1.scss': 'html { body { font: sans-serif; } }',
});
return readSync(inputNode.outputPath);
})
.then(() => sass.build())
.then(() => {
expect(sass.render.callCount).to.equal(2);
});
});
});