'use strict';

var fs = require('fs'),
  path = require('path'),
  childProcess = require('child_process'),
  chai = require('chai'),
  Q = require('q'),
  del = require('del'),
  chalk = require('chalk'),
  tmp = require('os').tmpDir(),
  currentDir = path.resolve(__dirname),
  styleGuideDir = path.resolve(currentDir, '../../'),
  testDir = path.join(tmp, 'sc5-package-smoketest' + Date.now()),
  sharedSrc = path.resolve(currentDir, '../projects/shared-css/'),
  npmSgDir = path.join(testDir, 'node_modules/sc5-styleguide'),
  MINUTE = 60000;

chai.config.includeStack = true;

describe('npm package executable', function() {

  before(function(done) {
    this.timeout(3 * MINUTE);
    prepareTestDir().then(runNpmInstall).then(done).catch(done);
  });

  after(cleanUp);

  it('generates style guide from SASS test project without errors', function(done) {
    this.timeout(30000);
    generateScssTestProjectStyleGuide().then(done).catch(done);
  });

  it('generates style guide from LESS test project without errors', function(done) {
    this.timeout(30000);
    generateLessTestProjectStyleGuide().then(done).catch(done);
  });

  it('generates demo style guide without errors', function(done) {
    this.timeout(30000);
    generateDemoStyleGuide().then(done).catch(done);
  });

});

function prepareTestDir() {
  console.log(chalk.yellow('\n  Creating temp dir', testDir));
  return Q.promise(function(resolve, reject) {
    fs.mkdirSync(testDir);
    fs.createReadStream(path.join(currentDir, 'npm-package-test.json'))
      .pipe(fs.createWriteStream(path.join(testDir, 'package.json')))
      .on('close', resolve)
      .on('error', function(err) {
        reject(err.toString());
      });
  });
}

function runNpmInstall() {
  console.log(chalk.yellow('\n  Installing npm module to temp dir from', styleGuideDir));
  var args = ['install', styleGuideDir, '--color', 'true', '--spin', 'true'],
    opts = {
      cwd: testDir,
      env: process.env,
      stdio: 'inherit'
    };
  return spawn('npm', args, opts);
}

function generateScssTestProjectStyleGuide() {
  var sassSrc = path.resolve(currentDir, '../projects/scss-project/source/**/*.scss'),
    sassDest = path.join(testDir, 'scss-test-output'),
    args = ['-s', sassSrc, '-s', sharedSrc, '-o', sassDest];
  return generateStyleGuide(args);
}

function generateLessTestProjectStyleGuide() {
  var lessSrc = path.resolve(currentDir, '../projects/less-project/source/**/*.less'),
    lessDest = path.join(testDir, 'less-test-output'),
    args = ['-s', lessSrc, '-s', sharedSrc, '-o', lessDest];
  return generateStyleGuide(args);
}

function generateDemoStyleGuide() {
  var src = path.resolve(npmSgDir, 'lib/app'),
    dest = path.join(testDir, 'demo-test-output'),
    conf = path.resolve(npmSgDir, 'lib/app/styleguide_config.json'),
    args = ['-s', src, '-o', dest, '-c', conf];
  return generateStyleGuide(args);
}

function generateStyleGuide(args) {
  var opts = {
      cwd: npmSgDir,
      env: process.env,
      stdio: [process.stdin, process.stdout, 'pipe']
    };
  return spawn('bin/styleguide', args, opts);
}

function deleteTempDir() {
  console.log(chalk.yellow('Cleaning up temp dir', testDir));
  del.sync(path.join(testDir, '*'), { force: true });
  del.sync(testDir, { force: true });
}

function cleanUp() {
  deleteTempDir();
}

function spawn(cmd, args, opts) {
  return Q.promise(function(resolve, reject) {
    var proc = childProcess.spawn(cmd, args, opts),
      rejectError = function(err) {
        reject(err.toString());
      };

    if (proc.stderr) {
      proc.stderr.on('data', rejectError);
    }
    proc.on('error', rejectError);
    proc.on('exit', function(code, signal) {
      if (code === 0) {
        resolve();
      } else {
        reject(code || signal);
      }
    });
  });
}

@@ -541,16 +541,43 @@
 60000;%0A%0A
+Q.longStackSupport = true;%0A
 chai.con
@@ -3389,16 +3389,62 @@
 opts) %7B%0A
+  var command = %5Bcmd%5D.concat(args).join(' ');%0A
   return
@@ -3490,16 +3490,34 @@
     var 
+error = '',%0A      
 proc = c
@@ -3560,20 +3560,18 @@
       re
-ject
+ad
 Error = 
@@ -3586,39 +3586,41 @@
 (err) %7B%0A        
-reject(
+error += 
 err.toString());
@@ -3609,33 +3609,32 @@
 = err.toString()
-)
 ;%0A      %7D;%0A%0A    
@@ -3679,28 +3679,26 @@
 n('data', re
-ject
+ad
 Error);%0A    
@@ -3703,43 +3703,8 @@
   %7D%0A
-    proc.on('error', rejectError);%0A
     
@@ -3821,22 +3821,102 @@
 ect(
-code %7C%7C signal
+new Error('Command exited with non-zero: ' + (code %7C%7C signal) + '%5Cn' + command + '%5Cn' + error)
 );%0A 

Improve npm package integration test failure messages