| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804 | var assert = require('assert'),  fs = require('fs'),  path = require('path'),  read = require('fs').readFileSync,  glob = require('glob'),  rimraf = require('rimraf'),  stream = require('stream'),  spawn = require('cross-spawn'),  cli = path.join(__dirname, '..', 'bin', 'node-sass'),  fixture = path.join.bind(null, __dirname, 'fixtures');describe('cli', function() {  // For some reason we experience random timeout failures in CI  // due to spawn hanging/failing silently. See #1692.  this.retries(4);  describe('node-sass < in.scss', function() {    it('should read data from stdin', function(done) {      var src = fs.createReadStream(fixture('simple/index.scss'));      var expected = read(fixture('simple/expected.css'), 'utf8').trim();      var bin = spawn(cli);      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.equal(data.trim(), expected.replace(/\r\n/g, '\n'));        done();      });      src.pipe(bin.stdin);    });    it('should compile sass using the --indented-syntax option', function(done) {      var src = fs.createReadStream(fixture('indent/index.sass'));      var expected = read(fixture('indent/expected.css'), 'utf8').trim();      var bin = spawn(cli, ['--indented-syntax']);      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.equal(data.trim(), expected.replace(/\r\n/g, '\n'));        done();      });      src.pipe(bin.stdin);    });    it('should compile with the --quiet option', function(done) {      var src = fs.createReadStream(fixture('simple/index.scss'));      var expected = read(fixture('simple/expected.css'), 'utf8').trim();      var bin = spawn(cli, ['--quiet']);      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.equal(data.trim(), expected.replace(/\r\n/g, '\n'));        done();      });      src.pipe(bin.stdin);    });    it('should compile with the --output-style option', function(done) {      var src = fs.createReadStream(fixture('compressed/index.scss'));      var expected = read(fixture('compressed/expected.css'), 'utf8').trim();      var bin = spawn(cli, ['--output-style', 'compressed']);      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.equal(data.trim(), expected.replace(/\r\n/g, '\n'));        done();      });      src.pipe(bin.stdin);    });    it('should compile with the --source-comments option', function(done) {      var src = fs.createReadStream(fixture('source-comments/index.scss'));      var expected = read(fixture('source-comments/expected.css'), 'utf8').trim();      var bin = spawn(cli, ['--source-comments']);      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.equal(data.trim(), expected.replace(/\r\n/g, '\n'));        done();      });      src.pipe(bin.stdin);    });    it('should render with indentWidth and indentType options', function(done) {      var src = new stream.Readable();      var bin = spawn(cli, ['--indent-width', 7, '--indent-type', 'tab']);      src._read = function() { };      src.push('div { color: transparent; }');      src.push(null);      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.equal(data.trim(), 'div {\n\t\t\t\t\t\t\tcolor: transparent; }');        done();      });      src.pipe(bin.stdin);    });    it('should render with linefeed option', function(done) {      var src = new stream.Readable();      var bin = spawn(cli, ['--linefeed', 'lfcr']);      src._read = function() { };      src.push('div { color: transparent; }');      src.push(null);      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.equal(data.trim(), 'div {\n\r  color: transparent; }');        done();      });      src.pipe(bin.stdin);    });  });  describe('node-sass in.scss', function() {    it('should compile a scss file', function(done) {      process.chdir(fixture('simple'));      var src = fixture('simple/index.scss');      var dest = fixture('simple/index.css');      var bin = spawn(cli, [src, dest]);      bin.once('close', function() {        assert(fs.existsSync(dest));        fs.unlinkSync(dest);        process.chdir(__dirname);        done();      });    });    it('should compile a scss file to custom destination', function(done) {      process.chdir(fixture('simple'));      var src = fixture('simple/index.scss');      var dest = fixture('simple/index-custom.css');      var bin = spawn(cli, [src, dest]);      bin.once('close', function() {        assert(fs.existsSync(dest));        fs.unlinkSync(dest);        process.chdir(__dirname);        done();      });    });    it('should compile with the --include-path option', function(done) {      var includePaths = [        '--include-path', fixture('include-path/functions'),        '--include-path', fixture('include-path/lib')      ];      var src = fixture('include-path/index.scss');      var expected = read(fixture('include-path/expected.css'), 'utf8').trim();      var bin = spawn(cli, [src].concat(includePaths));      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.equal(data.trim(), expected.replace(/\r\n/g, '\n'));        done();      });    });    it('should compile silently using the --quiet option', function(done) {      process.chdir(fixture('simple'));      var src = fixture('simple/index.scss');      var dest = fixture('simple/index.css');      var bin = spawn(cli, [src, dest, '--quiet']);      var didEmit = false;      bin.stderr.once('data', function() {        didEmit = true;      });      bin.once('close', function() {        assert.equal(didEmit, false);        fs.unlinkSync(dest);        process.chdir(__dirname);        done();      });    });    it('should still report errors with the --quiet option', function(done) {      process.chdir(fixture('invalid'));      var src = fixture('invalid/index.scss');      var dest = fixture('invalid/index.css');      var bin = spawn(cli, [src, dest, '--quiet']);      var didEmit = false;      bin.stderr.once('data', function() {        didEmit = true;      });      bin.once('close', function() {        assert.equal(didEmit, true);        process.chdir(__dirname);        done();      });    });    it('should not exit with the --watch option', function(done) {      var src = fixture('simple/index.scss');      var bin = spawn(cli, [src, '--watch']);      var exited;      bin.once('close', function() {        exited = true;      });      setTimeout(function() {        if (exited) {          throw new Error('Watch ended too early!');        } else {          bin.kill();          done();        }      }, 100);    });    it.skip('should emit `warn` on file change when using --watch option', function(done) {      var src = fixture('simple/tmp.scss');      fs.writeFileSync(src, '');      var bin = spawn(cli, ['--watch', src]);      bin.stderr.setEncoding('utf8');      bin.stderr.once('data', function(data) {        assert.strictEqual(data.trim(), '=> changed: ' + src);        fs.unlinkSync(src);        bin.kill();        done();      });      setTimeout(function() {        fs.appendFileSync(src, 'body {}');      }, 500);    });    it.skip('should emit nothing on file change when using --watch and --quiet options', function(done) {      var src = fixture('simple/tmp.scss');      var didEmit = false;      fs.writeFileSync(src, '');      var bin = spawn(cli, ['--watch', '--quiet', src]);      bin.stderr.setEncoding('utf8');      bin.stderr.once('data', function() {        didEmit = true;      });      setTimeout(function() {        fs.appendFileSync(src, 'body {}');        setTimeout(function() {          assert.equal(didEmit, false);          bin.kill();          done();          fs.unlinkSync(src);        }, 200);      }, 500);    });    it.skip('should render all watched files', function(done) {      var src = fixture('simple/bar.scss');      fs.writeFileSync(src, '');      var bin = spawn(cli, [        '--output-style', 'compressed',        '--watch', src      ]);      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.strictEqual(data.trim(), 'body{background:white}');        fs.unlinkSync(src);        bin.kill();        done();      });      setTimeout(function() {        fs.appendFileSync(src, 'body{background:white}');      }, 500);    });    it.skip('should watch the full scss dep tree for a single file (scss)', function(done) {      var src = fixture('watching/index.scss');      var foo = fixture('watching/white.scss');      fs.writeFileSync(foo, '');      var bin = spawn(cli, [        '--output-style', 'compressed',        '--watch', src      ]);      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.strictEqual(data.trim(), 'body{background:blue}');        bin.kill();        done();      });      setTimeout(function() {        fs.appendFileSync(foo, 'body{background:blue}\n');      }, 500);    });    it.skip('should watch the full sass dep tree for a single file (sass)', function(done) {      var src = fixture('watching/index.sass');      var foo = fixture('watching/bar.sass');      fs.writeFileSync(foo, '');      var bin = spawn(cli, [        '--output-style', 'compressed',        '--watch', src      ]);      bin.stdout.setEncoding('utf8');      bin.stdout.once('data', function(data) {        assert.strictEqual(data.trim(), 'body{background:red}');        bin.kill();        done();      });      setTimeout(function() {        fs.appendFileSync(foo, 'body\n\tbackground: red\n');      }, 500);    });  });  describe('node-sass --output directory', function() {    it.skip('should watch whole directory', function(done) {      var destDir = fixture('watching-css-out-01/');      var srcDir = fixture('watching-dir-01/');      var srcFile = path.join(srcDir, 'index.scss');      fs.writeFileSync(srcFile, '');      var bin = spawn(cli, [        '--output-style', 'compressed',        '--output', destDir,        '--watch', srcDir      ]);      setTimeout(function() {        fs.appendFileSync(srcFile, 'a {color:green;}\n');        setTimeout(function() {          bin.kill();          var files = fs.readdirSync(destDir);          assert.deepEqual(files, ['index.css']);          rimraf(destDir, done);        }, 200);      }, 500);    });    it.skip('should compile all changed files in watched directory', function(done) {      var destDir = fixture('watching-css-out-02/');      var srcDir = fixture('watching-dir-02/');      var srcFile = path.join(srcDir, 'foo.scss');      fs.writeFileSync(srcFile, '');      var bin = spawn(cli, [        '--output-style', 'compressed',        '--output', destDir,        '--watch', srcDir      ]);      setTimeout(function () {        fs.appendFileSync(srcFile, 'body{background:white}\n');        setTimeout(function () {          bin.kill();          var files = fs.readdirSync(destDir);          assert.deepEqual(files, ['foo.css', 'index.css']);          rimraf(destDir, done);        }, 200);      }, 500);    });  });  describe('node-sass in.scss --output out.css', function() {    it('should compile a scss file to build.css', function(done) {      var src = fixture('simple/index.scss');      var dest = fixture('simple/index.css');      var bin = spawn(cli, [src, '--output', path.dirname(dest)]);      bin.once('close', function() {        assert(fs.existsSync(dest));        fs.unlinkSync(dest);        done();      });    });    it('should compile with the --source-map option', function(done) {      var src = fixture('source-map/index.scss');      var destCss = fixture('source-map/index.css');      var destMap = fixture('source-map/index.map');      var expectedCss = read(fixture('source-map/expected.css'), 'utf8').trim().replace(/\r\n/g, '\n');      var expectedMap = read(fixture('source-map/expected.map'), 'utf8').trim().replace(/\r\n/g, '\n');      var bin = spawn(cli, [src, '--output', path.dirname(destCss), '--source-map', destMap]);      bin.once('close', function() {        assert.equal(read(destCss, 'utf8').trim(), expectedCss);        assert.equal(read(destMap, 'utf8').trim(), expectedMap);        fs.unlinkSync(destCss);        fs.unlinkSync(destMap);        done();      });    });    it('should omit sourceMappingURL if --omit-source-map-url flag is used', function(done) {      var src = fixture('source-map/index.scss');      var dest = fixture('source-map/index.css');      var map = fixture('source-map/index.map');      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--source-map', map, '--omit-source-map-url'      ]);      bin.once('close', function() {        assert.strictEqual(read(dest, 'utf8').indexOf('sourceMappingURL'), -1);        assert(fs.existsSync(map));        fs.unlinkSync(map);        fs.unlinkSync(dest);        done();      });    });    it('should compile with the --source-root option', function(done) {      var src = fixture('source-map/index.scss');      var destCss = fixture('source-map/index.css');      var destMap = fixture('source-map/index.map');      var expectedCss = read(fixture('source-map/expected.css'), 'utf8').trim().replace(/\r\n/g, '\n');      var expectedUrl = 'http://test/';      var bin = spawn(cli, [        src, '--output', path.dirname(destCss),        '--source-map-root', expectedUrl,        '--source-map', destMap      ]);      bin.once('close', function() {        assert.equal(read(destCss, 'utf8').trim(), expectedCss);        assert.equal(JSON.parse(read(destMap, 'utf8')).sourceRoot, expectedUrl);        fs.unlinkSync(destCss);        fs.unlinkSync(destMap);        done();      });    });    it('should compile with the --source-map-embed option and no outfile', function(done) {      var src = fixture('source-map-embed/index.scss');      var expectedCss = read(fixture('source-map-embed/expected.css'), 'utf8').trim().replace(/\r\n/g, '\n');      var result = '';      var bin = spawn(cli, [        src,        '--source-map-embed',        '--source-map', 'true'      ]);      bin.stdout.on('data', function(data) {        result += data;      });      bin.once('close', function() {        assert.equal(result.trim().replace(/\r\n/g, '\n'), expectedCss);        done();      });    });  });  describe('node-sass sass/ --output css/', function() {    it('should create the output directory', function(done) {      var src = fixture('input-directory/sass');      var dest = fixture('input-directory/css');      var bin = spawn(cli, [src, '--output', dest]);      bin.once('close', function() {        assert(fs.existsSync(dest));        rimraf.sync(dest);        done();      });    });    it('should compile all files in the folder', function(done) {      var src = fixture('input-directory/sass');      var dest = fixture('input-directory/css');      var bin = spawn(cli, [src, '--output', dest]);      bin.once('close', function() {        var files = fs.readdirSync(dest).sort();        assert.deepEqual(files, ['one.css', 'two.css', 'nested'].sort());        var nestedFiles = fs.readdirSync(path.join(dest, 'nested'));        assert.deepEqual(nestedFiles, ['three.css']);        rimraf.sync(dest);        done();      });    });    it('should compile with --source-map set to directory', function(done) {      var src = fixture('input-directory/sass');      var dest = fixture('input-directory/css');      var destMap = fixture('input-directory/map');      var bin = spawn(cli, [src, '--output', dest, '--source-map', destMap]);      bin.once('close', function() {        var map = JSON.parse(read(fixture('input-directory/map/nested/three.css.map'), 'utf8'));        assert.equal(map.file, '../../css/nested/three.css');        rimraf.sync(dest);        rimraf.sync(destMap);        done();      });    });    it('should skip files with an underscore', function(done) {      var src = fixture('input-directory/sass');      var dest = fixture('input-directory/css');      var bin = spawn(cli, [src, '--output', dest]);      bin.once('close', function() {        var files = fs.readdirSync(dest);        assert.equal(files.indexOf('_skipped.css'), -1);        rimraf.sync(dest);        done();      });    });    it('should ignore nested files if --recursive false', function(done) {      var src = fixture('input-directory/sass');      var dest = fixture('input-directory/css');      var bin = spawn(cli, [        src, '--output', dest,        '--recursive', false      ]);      bin.once('close', function() {        var files = fs.readdirSync(dest);        assert.deepEqual(files, ['one.css', 'two.css']);        rimraf.sync(dest);        done();      });    });    it('should error if no output directory is provided', function(done) {      var src = fixture('input-directory/sass');      var bin = spawn(cli, [src]);      bin.once('close', function(code) {        assert.notStrictEqual(code, 0);        assert.strictEqual(glob.sync(fixture('input-directory/**/*.css')).length, 0);        done();      });    });    it('should error if output directory is not a directory', function(done) {      var src = fixture('input-directory/sass');      var dest = fixture('input-directory/sass/one.scss');      var bin = spawn(cli, [src, '--output', dest]);      bin.once('close', function(code) {        assert.notStrictEqual(code, 0);        assert.equal(glob.sync(fixture('input-directory/**/*.css')).length, 0);        done();      });    });    it('should not error if output directory is a symlink', function(done) {      var outputDir = fixture('input-directory/css');      var src = fixture('input-directory/sass');      var symlink = fixture('symlinked-css');      fs.mkdirSync(outputDir);      fs.symlinkSync(outputDir, symlink);      var bin = spawn(cli, [src, '--output', symlink]);      bin.once('close', function() {        var files = fs.readdirSync(outputDir).sort();        assert.deepEqual(files, ['one.css', 'two.css', 'nested'].sort());        var nestedFiles = fs.readdirSync(path.join(outputDir, 'nested'));        assert.deepEqual(nestedFiles, ['three.css']);        rimraf.sync(outputDir);        fs.unlinkSync(symlink);        done();      });    });  });  describe('node-sass in.scss --output path/to/file/out.css', function() {    it('should create the output directory', function(done) {      var src = fixture('output-directory/index.scss');      var dest = fixture('output-directory/path/to/file/index.css');      var bin = spawn(cli, [src, '--output', path.dirname(dest)]);      bin.once('close', function() {        assert(fs.existsSync(path.dirname(dest)));        fs.unlinkSync(dest);        fs.rmdirSync(path.dirname(dest));        dest = path.dirname(dest);        fs.rmdirSync(path.dirname(dest));        dest = path.dirname(dest);        fs.rmdirSync(path.dirname(dest));        done();      });    });  });  describe('node-sass --follow --output output-dir input-dir', function() {    it('should compile with the --follow option', function(done) {      var src = fixture('follow/input-dir');      var dest = fixture('follow/output-dir');      fs.mkdirSync(src);      fs.symlinkSync(path.join(path.dirname(src), 'foo'), path.join(src, 'foo'), 'dir');      var bin = spawn(cli, [src, '--follow', '--output', dest]);      bin.once('close', function() {        var expected = path.join(dest, 'foo/bar/index.css');        fs.unlinkSync(path.join(src, 'foo'));        fs.rmdirSync(src);        assert(fs.existsSync(expected));        fs.unlinkSync(expected);        expected = path.dirname(expected);        fs.rmdirSync(expected);        expected = path.dirname(expected);        fs.rmdirSync(expected);        fs.rmdirSync(dest);        done();      });    });  });  describe('importer', function() {    var dest = fixture('include-files/index.css');    var src = fixture('include-files/index.scss');    var expected = read(fixture('include-files/expected-importer.css'), 'utf8').trim().replace(/\r\n/g, '\n');    it('should override imports and fire callback with file and contents', function(done) {      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--importer', fixture('extras/my_custom_importer_file_and_data_cb.js')      ]);      bin.once('close', function() {        assert.equal(read(dest, 'utf8').trim(), expected);        fs.unlinkSync(dest);        done();      });    });    it('should override imports and fire callback with file', function(done) {      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--importer', fixture('extras/my_custom_importer_file_cb.js')      ]);      bin.once('close', function() {        if (fs.existsSync(dest)) {          assert.equal(read(dest, 'utf8').trim(), '');          fs.unlinkSync(dest);        }        done();      });    });    it('should override imports and fire callback with data', function(done) {      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--importer', fixture('extras/my_custom_importer_data_cb.js')      ]);      bin.once('close', function() {        assert.equal(read(dest, 'utf8').trim(), expected);        fs.unlinkSync(dest);        done();      });    });    it('should override imports and return file and contents', function(done) {      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--importer', fixture('extras/my_custom_importer_file_and_data.js')      ]);      bin.once('close', function() {        assert.equal(read(dest, 'utf8').trim(), expected);        fs.unlinkSync(dest);        done();      });    });    it('should override imports and return file', function(done) {      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--importer', fixture('extras/my_custom_importer_file.js')      ]);      bin.once('close', function() {        if (fs.existsSync(dest)) {          assert.equal(read(dest, 'utf8').trim(), '');          fs.unlinkSync(dest);        }        done();      });    });    it('should override imports and return data', function(done) {      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--importer', fixture('extras/my_custom_importer_data.js')      ]);      bin.once('close', function() {        assert.equal(read(dest, 'utf8').trim(), expected);        fs.unlinkSync(dest);        done();      });    });    it('should accept arrays of importers and return respect the order', function(done) {      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--importer', fixture('extras/my_custom_arrays_of_importers.js')      ]);      bin.once('close', function() {        assert.equal(read(dest, 'utf8').trim(), expected);        fs.unlinkSync(dest);        done();      });    });    it('should return error for invalid importer file path', function(done) {      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--importer', fixture('non/existing/path')      ]);      bin.once('close', function(code) {        assert.notStrictEqual(code, 0);        done();      });    });    it('should reflect user-defined Error', function(done) {      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--importer', fixture('extras/my_custom_importer_error.js')      ]);      bin.stderr.once('data', function(code) {        assert.equal(JSON.parse(code).message, 'doesn\'t exist!');        done();      });    });  });  describe('functions', function() {    it('should let custom functions call setter methods on wrapped sass values (number)', function(done) {      var dest = fixture('custom-functions/setter.css');      var src = fixture('custom-functions/setter.scss');      var expected = read(fixture('custom-functions/setter-expected.css'), 'utf8').trim().replace(/\r\n/g, '\n');      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--functions', fixture('extras/my_custom_functions_setter.js')      ]);      bin.once('close', function() {        assert.equal(read(dest, 'utf8').trim(), expected);        fs.unlinkSync(dest);        done();      });    });    it('should properly convert strings when calling custom functions', function(done) {      var dest = fixture('custom-functions/string-conversion.css');      var src = fixture('custom-functions/string-conversion.scss');      var expected = read(fixture('custom-functions/string-conversion-expected.css'), 'utf8').trim().replace(/\r\n/g, '\n');      var bin = spawn(cli, [        src, '--output', path.dirname(dest),        '--functions', fixture('extras/my_custom_functions_string_conversion.js')      ]);      bin.once('close', function() {        assert.equal(read(dest, 'utf8').trim(), expected);        fs.unlinkSync(dest);        done();      });    });  });});
 |