<|start_filename|>lib/commands/nw-test/runner.js<|end_filename|>
'use strict';

var spawn = require('child_process').spawn;
var argv = require('optimist').argv;

runNw(argv['nw-path'], argv['tests-path']);

function runNw(nwPath, testsPath) {
  var nw = spawn(nwPath, [testsPath]);
  var hasErrors = false;

  // Cleanup nw.js output to be TAP (test anything protocol) compliant
  nw.stderr.on('data', function(data) {
    data = data.toString('utf8');

    if (data.indexOf('[qunit-logger]') > -1) {
      data = data.replace(/.*\[qunit-logger] (.*)"", source:.*/g, '$1');
      data = data.replace(/\\"/g, '"');
      data = data.replace(/\\\\n/g, '\\n');
      process.stdout.write(data);

      if (data === '# done with errors') {
        hasErrors = true;
      }

      if (data.indexOf('# done') > -1) {
        nw.kill();
        process.exit(hasErrors ? 1 : 0);
      }
    }
  });

  process.on('SIGTERM', function() {
    nw.kill();
  });
}


<|start_filename|>tests/helpers/mocks/nw-builder.js<|end_filename|>
'use strict';

var RSVP = require('rsvp');

function MockNWBuilder(options) {
  this.options = options;
}

module.exports = MockNWBuilder;

MockNWBuilder.prototype.on = function() {
};

MockNWBuilder.prototype.build = function() {
  return RSVP.resolve(this.options);
};


<|start_filename|>tests/unit/commands/nw-package-test.js<|end_filename|>
'use strict';

var path          = require('path');
var mockery       = require('mockery');
var RSVP          = require('rsvp');
var sinon         = require('sinon');
var Command       = require('ember-cli/lib/models/command');
var Task          = require('ember-cli/lib/models/task');
var MockUI        = require('ember-cli/tests/helpers/mock-ui');
var MockAnalytics = require('ember-cli/tests/helpers/mock-analytics');
var MockNWBuilder = require('../../helpers/mocks/nw-builder');
var MockProject   = require('../../helpers/mocks/project');
var expect        = require('../../helpers/expect');

describe("ember nw:package command", function() {
  var CommandUnderTest, commandOptions, nwBuild;

  before(function() {
    mockery.enable({
      useCleanCache: true,
      warnOnUnregistered: false
    });

    mockery.registerMock('nw-builder', MockNWBuilder);
    mockery.registerMock('../helpers/detect-platform', function() {
      return 'osx64';
    });

    var cmd = require('../../../lib/commands/nw-package');
    CommandUnderTest = Command.extend(cmd);
  });

  after(function() {
    mockery.disable();
  });

  beforeEach(function() {
    commandOptions = {
      ui: new MockUI(),
      analytics: new MockAnalytics(),
      settings: {},
      project: new MockProject('project-empty', {
        dependencies: {
          rsvp: '^3.0.17',
          wrench: '^1.5.8'
        }
      }),
      tasks: {
        Build: Task.extend({
          run: function() {
            return RSVP.resolve();
          }
        })
      }
    };

    nwBuild = sinon.spy(MockNWBuilder.prototype, 'build');
  });

  afterEach(function() {
    MockNWBuilder.prototype.build.restore();
  });

  it("should build the project before packaging", function() {
    var tasks = [];

    commandOptions.tasks = {
      Build: Task.extend({
        run: function() {
          tasks.push('build');
          return RSVP.resolve();
        }
      })
    };

    commandOptions.packageApp = function() {
      tasks.push('package');
      return RSVP.resolve();
    };

    var command = new CommandUnderTest(commandOptions).validateAndRun();

    return expect(command).to.be.fulfilled
      .then(function() {
        expect(tasks).to.deep.equal(['build', 'package']);
      });
  });

  it("should package the app with the right default configuration", function() {
    var command = new CommandUnderTest(commandOptions).validateAndRun();

    return expect(command).to.be.fulfilled
      .then(function(options) {
        expect(nwBuild.calledOnce).to.be.true;

        var defaultConfig = {
          buildDir: 'build/app',
          cacheDir: 'build/cache',
          files: [
            'package.json',
            'dist/**',
            'node_modules/rsvp/**',
            'node_modules/wrench/**'
          ],
          platforms: ['osx64']
        };

        var promise = nwBuild.returnValues[0];
        return expect(promise).to.eventually.deep.equal(defaultConfig);
      });
  });

  it("should package the app with the configuration specified by the default config file", function() {
    commandOptions.project = new MockProject('project-with-config');

    var command = new CommandUnderTest(commandOptions).validateAndRun();

    return expect(command).to.be.fulfilled
      .then(function(options) {
        expect(nwBuild.calledOnce).to.be.true;

        var customConfig = {
          buildDir: 'build',
          cacheDir: 'cache',
          files: ['package.json'],
          platforms: ['osx64', 'win64'],
          appName: 'foo',
          appVersion: '0.0.1'
        };

        var promise = nwBuild.returnValues[0];
        return expect(promise).to.eventually.deep.equal(customConfig);
      });
  });

  it("should package the app with the configuration specified by the custom config file", function() {
    commandOptions.project = new MockProject('project-with-config');

    var command = new CommandUnderTest(commandOptions).validateAndRun(['--config-file=custom-nw-package.js']);

    return expect(command).to.be.fulfilled
      .then(function(options) {
        expect(nwBuild.calledOnce).to.be.true;

        var customConfig = {
          buildDir: 'build/release',
          cacheDir: 'build/cache',
          files: [
            'package.json',
            'dist/**'
          ],
          platforms: ['win64'],
          appName: 'bar',
          appVersion: '0.0.1'
        };

        var promise = nwBuild.returnValues[0];
        return expect(promise).to.eventually.deep.equal(customConfig);
      });
  });
});


<|start_filename|>client/nwjs/reload.js<|end_filename|>
/* jshint browser: true */
(function() {
  if (!window.nwDispatcher && !window.nw) { return; }

  // Reload the page when anything in `dist` changes
  var fs = window.requireNode('fs');
  var watchDir = './dist';

  if (fs.existsSync(watchDir)) {
    fs.watch(watchDir, function() {
      window.location.reload();
    });
  }
})();


<|start_filename|>index.js<|end_filename|>
'use strict';

var fs = require('fs');
var path = require('path');

function injectScript(scriptName) {
  var filePath = path.join(__dirname, 'lib', 'resources', scriptName);
  return '<script>\n' + fs.readFileSync(filePath, { encoding: 'utf8' }) + '\n</script>';
}

module.exports = {
  name: 'ember-cli-nwjs',

  included: function(app) {
    this._super.included(app);

    if (!process.env.EMBER_CLI_NW) { return; }

    app.import({ development: 'vendor/nwjs/reload.js' });
    if (process.env.NW_TESTS_DEV) {
      app.import({ test: 'vendor/nwjs/browser-qunit-adapter.js' });
    } else {
      app.import({ test: 'vendor/nwjs/tap-qunit-adapter.js' });
    }
  },

  includedCommands: function() {
    return {
      'nw': require('./lib/commands/nw'),
      'nw:package': require('./lib/commands/nw-package'),
      'nw:test': require('./lib/commands/nw-test')
    }
  },

  treeForVendor: function() {
    return path.join(__dirname, 'client');
  },

  postprocessTree: function(type, tree) {
    if (!process.env.EMBER_CLI_NW) {
      return tree;
    }

    if (type === 'all' && process.env.EMBER_ENV === 'test') {
      var funnel = require('broccoli-funnel');
      var mergeTrees = require('broccoli-merge-trees');
      var replace = require('broccoli-string-replace');

      // Update the base URL in `tests/index.html`
      var index = replace(tree, {
        files: ['tests/index.html'],
        pattern: {
          match: /base href="\/"/,
          replacement: 'base href="../"'
        }
      });

      // Copy `tests/package.json` to the output directory
      var testPkg = funnel('tests', {
        files: ['package.json'],
        destDir: '/tests'
      });

      var testPageOptions = process.env.NW_TEST_PAGE_OPTIONS;

      if (testPageOptions) {
        testPkg = replace(testPkg, {
          files: ['tests/package.json'],
          patterns: [{
            match: /"main":\s*"(index.html\?[^\"]+)"/,
            replacement: '"main": "$1&' + testPageOptions + '"'
          }, {
            match: /"main":\s*"index.html"/,
            replacement: '"main": "index.html?' + testPageOptions + '"'
          }]
        });
      }

      return mergeTrees([tree, index, testPkg], { overwrite: true });
    }

    return tree;
  },

  contentFor: function(type) {
    if (!process.env.EMBER_CLI_NW) { return; }

    if (type === 'head') {
      return injectScript('shim-head.js');
    }

    if (type === 'body-footer') {
      return injectScript('shim-footer.js');
    }

    if (type === 'test-body' && process.env.EMBER_ENV === 'test') {
      var testemServer = process.env.NW_TESTEM_SERVER_URL;
      if (testemServer) {
        return '<script src="' + testemServer + '/socket.io/socket.io.js"></script>';
      }
    }
  }
};


<|start_filename|>tests/unit/commands/nw-test.js<|end_filename|>
'use strict';

var path          = require('path');
var mockery       = require('mockery');
var mockSpawn     = require('mock-spawn');
var RSVP          = require('rsvp');
var Command       = require('ember-cli/lib/models/command');
var Task          = require('ember-cli/lib/models/task');
var MockUI        = require('ember-cli/tests/helpers/mock-ui');
var MockAnalytics = require('ember-cli/tests/helpers/mock-analytics');
var MockProject   = require('../../helpers/mocks/project');
var expect        = require('../../helpers/expect');

describe("ember nw command", function() {
  var CommandUnderTest, commandOptions, spawn, _envNW;

  before(function() {
    mockery.enable({
      useCleanCache: true,
      warnOnUnregistered: false
    });
  });

  after(function() {
    mockery.disable();
  });

  beforeEach(function() {
    _envNW = process.env.NW_PATH;
    delete process.env.NW_PATH;

    spawn = mockSpawn();
    mockery.registerMock('child_process', { spawn: spawn });

    var cmd = require('../../../lib/commands/nw');
    CommandUnderTest = Command.extend(cmd);

    commandOptions = {
      ui: new MockUI(),
      analytics: new MockAnalytics(),
      settings: {},
      project: new MockProject('project-empty')
    };
  });

  afterEach(function() {
    if (_envNW) {
      process.env.NW_PATH = _envNW;
    } else {
      delete process.env.NW_PATH;
    }

    mockery.deregisterAll();
    mockery.resetCache();
  });

  it("should build the project before running nw.js", function() {
    var tasks = [];

    commandOptions.buildWatch = function() {
      tasks.push('buildWatch');
      return RSVP.resolve();
    };

    commandOptions.runNW = function() {
      tasks.push('runNW');
      return RSVP.resolve();
    };

    var command = new CommandUnderTest(commandOptions).validateAndRun();

    return expect(command).to.be.fulfilled
      .then(function() {
        expect(tasks).to.deep.equal(['buildWatch', 'runNW']);
      });
  });

  it("should not run nw.js when the build fails", function() {
    var tasks = [];

    commandOptions.buildWatch = function() {
      tasks.push('buildWatch');
      return RSVP.reject();
    };

    commandOptions.runNW = function() {
      tasks.push('runNW');
      return RSVP.resolve();
    };

    var command = new CommandUnderTest(commandOptions).validateAndRun();

    return expect(command).to.be.rejected
      .then(function() {
        expect(tasks).to.deep.equal(['buildWatch']);
      });
  });

  it("should not keep watching if nw.js fails to run", function() {
    var tasks = [];

    commandOptions.buildWatch = function() {
      tasks.push('buildWatch');
      return RSVP.resolve();
    };

    commandOptions.runNW = function() {
      tasks.push('runNW');
      return RSVP.reject();
    };

    var command = new CommandUnderTest(commandOptions).validateAndRun();

    return expect(command).to.be.rejected
      .then(function() {
        expect(tasks).to.deep.equal(['buildWatch', 'runNW']);
      });
  });

  it("should spawn a 'nw' process with the right arguments", function() {
    commandOptions.buildWatch = function() {
      return RSVP.resolve();
    };

    var command = new CommandUnderTest(commandOptions).validateAndRun();
    var ui = commandOptions.ui;

    return expect(command).to.be.fulfilled
      .then(function() {
        expect(spawn.calls.length).to.equal(1);
        expect(spawn.calls[0].command).to.equal('nw');
        expect(spawn.calls[0].args).to.deep.equal(['.']);

        expect(ui.output).to.contain("Starting nw.js...");
        expect(ui.output).to.contain("nw.js exited.");
      });
  });

  it("should print a friendly message when the 'nw' command cannot be found", function() {
    commandOptions.buildWatch = function() {
      return RSVP.resolve();
    };

    var command = new CommandUnderTest(commandOptions).validateAndRun();
    var ui = commandOptions.ui;

    spawn.sequence.add(function() {
      this.emit('error', { code: 'ENOENT' });
    });

    return expect(command).to.be.rejected
      .then(function() {
        expect(spawn.calls.length).to.equal(1);
        expect(spawn.calls[0].command).to.equal('nw');
        expect(spawn.calls[0].args).to.deep.equal(['.']);

        expect(ui.output).to.contain("Starting nw.js...");
        expect(ui.output).to.contain("Error running the following command: nw");
        expect(ui.output).to.contain("re-run the blueprint");
      });
  });
});


<|start_filename|>client/nwjs/browser-qunit-adapter.js<|end_filename|>
(function(window) {
  // Exit immediately if we're not running in NW.js
  if (typeof window.nwDispatcher === 'undefined' && typeof window.nw === 'undefined') {
    return;
  }

  function setQUnitAdapter(serverURL) {
    var socket = io(serverURL);

    socket.on('connect', function() {
      // connected to testem server
      socket.emit('browser-login', 'NW.js', '1');
    });

    socket.on('start-tests', function() {
      // testem indicated we should re-run
      socket.disconnect();
      window.location.reload();
    });

    qunitAdapter(socket);
  }

  // Adapted from Testem's default qunit-adapter.
  function qunitAdapter(socket) {
    var currentTest, currentModule;

    var id = 1;

    var results = {
      failed: 0,
      passed: 0,
      total: 0,
      tests: []
    };

    QUnit.log(function(details) {
      var item = {
        passed: details.result,
        message: details.message
      }

      if (!details.result) {
        item.actual = details.actual;
        item.expected = details.expected;
      }

      currentTest.items.push(item);
    });

    QUnit.testStart(function(details) {
      currentTest = {
        id: id++,
        name: (currentModule ? currentModule + ': ' : '') + details.name,
        items: []
      };
      socket.emit('tests-start');
    });

    QUnit.testDone(function(details) {
      currentTest.failed = details.failed;
      currentTest.passed = details.passed;
      currentTest.total = details.total;

      results.total++;

      if (currentTest.failed > 0) {
        results.failed++;
      } else {
        results.passed++;
      }

      results.tests.push(currentTest);
      socket.emit('test-result', currentTest);
    });

    QUnit.moduleStart(function(details) {
      currentModule = details.name;
    });

    QUnit.done(function(details) {
      results.runDuration = details.runtime;
      socket.emit('all-test-results', results);
    });
  }

  window.addEventListener('load', function() {
    setQUnitAdapter(process.env.NW_TESTEM_SERVER_URL);
  });
}(this));


<|start_filename|>client/nwjs/tap-qunit-adapter.js<|end_filename|>
(function(window) {
  // Exit immediately if we're not running in NW.js
  if (typeof window.nwDispatcher === 'undefined' && typeof window.nw === 'undefined') {
    return;
  }

  // Log QUnit results to the console so they show up
  // in the `nw` process output.
  function log(content) {
    console.log('[qunit-logger] ' + content);
  }

  function setQUnitAdapter() {
    var testCount = 0;

    QUnit.begin(function(details) {
      if (details.totalTests >= 1) {
        log('1..' + details.totalTests);
      }
    });

    QUnit.testDone(function(details) {
      testCount++;
      if (details.failed === 0) {
        log('ok ' + testCount + ' - ' + details.module + ' # ' + details.name);
      }
    });

    QUnit.log(function(details) {
      if (details.result !== true) {
        var actualTestCount = testCount + 1;
        log('# ' + JSON.stringify(details));
        log('not ok ' + actualTestCount + ' - ' + details.module + ' - ' + details.name);
      }
    });

    QUnit.done(function(details) {
      log('# done' + (details.failed === 0 ? '' : ' with errors'));
    });
  }

  window.addEventListener('load', setQUnitAdapter);
}(this));


<|start_filename|>lib/commands/nw-package.js<|end_filename|>
'use strict';

var fs        = require('fs');
var path      = require('path');
var chalk     = require('chalk');
var NwBuilder = require('nw-builder');

module.exports = {
  name: 'nw:package',
  description: 'Packages your NW.js app',

  availableOptions: [
    { name: 'environment', type: String, default: 'production', aliases: ['e', { 'dev': 'development' }, { 'prod': 'production' }] },
    { name: 'output-path', type: String, default: 'dist/', aliases: ['o'] },
    { name: 'config-file', type: String, default: 'config/nw-package.js', aliases: ['f'] }
  ],

  init: function() {
    this._super.init && this._super.init.apply(this, arguments);
    process.env.EMBER_CLI_NW = true;
  },

  buildApp: function(options) {
    var buildTask = new this.tasks.Build({
      ui: this.ui,
      analytics: this.analytics,
      project: this.project
    });

    return buildTask.run(options);
  },

  packageApp: function(options) {
    var ui = this.ui;

    ui.writeLine(chalk.green('Packaging...'));

    var config = this.nwConfig(options);
    var nw = new NwBuilder(config);
    nw.on('log', console.log);

    return nw.build().then(function() {
      ui.writeLine(chalk.green('Packaged project successfully.'));
    });
  },

  nwConfig: function(options) {
    var config = {};

    var configFile = path.resolve(this.project.root, options.configFile);
    if (fs.existsSync(configFile)) {
      config = require(configFile);
    }

    if (!config.buildDir) {
      config.buildDir = 'build/app';
    }

    if (!config.cacheDir) {
      config.cacheDir = 'build/cache';
    }

    if (!config.files) {
      config.files = this.nwFiles(options);
    }

    if (!config.platforms) {
      var detectPlatform = require('../helpers/detect-platform');
      var currentPlatform = detectPlatform();
      if (currentPlatform) {
        config.platforms = [currentPlatform];
      }
    }

    return config;
  },

  nwFiles: function(options) {
    var nwFiles = ['package.json'];

    // Contents of the build output directory
    nwFiles.push(options.outputPath.replace(/\/?$/, '/**'));

    // Non-dev NPM modules
    var npmModulesRoot = 'node_modules';
    var npmDependencies = this.project.pkg.dependencies || {};

    Object.keys(npmDependencies).forEach(function(dependency) {
      nwFiles.push(npmModulesRoot + '/' + dependency + '/**');
    });

    return nwFiles;
  },

  run: function(options) {
    var _this = this;

    return this.buildApp(options)
      .then(function() {
        return _this.packageApp(options);
      });
  }
};


<|start_filename|>tests/mocha-jshint-test.js<|end_filename|>
var mochaJSHint = require('mocha-jshint');

mochaJSHint({
  paths: ['lib', 'blueprints']
});


<|start_filename|>lib/resources/shim-footer.js<|end_filename|>
/* jshint browser: true */
(function(window) {
  if (!window.nwDispatcher && !window.nw) { return; }

  // Restore NW.js variables.
  global.window = window;
  window.process = global.process;

  // Redefine a global `require` function that can satisfy both
  // Node and AMD module systems.
  var requireAMD = window.require;
  var requireNode = window.requireNode;

  if (requireAMD) {
    window.require = function() {
      try {
        return requireAMD.apply(null, arguments);
      } catch (e) {
        return requireNode.apply(null, arguments);
      }
    };
  } else {
    window.require = requireNode;
  }
})(this);


<|start_filename|>ember-cli-build.js<|end_filename|>
var jscs       = require('broccoli-jscs');
var jshint     = require('broccoli-jshint');
var mergeTrees = require('broccoli-merge-trees');

module.exports = function() {
  var srcTree = mergeTrees(['lib', 'blueprints']);

  var jscsTree = jscs(srcTree);
  jscsTree.description = 'JSCS';

  var jshintTree = jshint(srcTree);

  return mergeTrees([
      jscsTree,
      jshintTree
    ], { overwrite: true });
};


<|start_filename|>tests/fixtures/project-with-config/custom-nw-package.js<|end_filename|>
module.exports = {
  buildDir: 'build/release',
  platforms: ['win64'],
  appName: 'bar',
  appVersion: '0.0.1'
};


<|start_filename|>blueprints/ember-cli-nwjs/index.js<|end_filename|>
'use strict';

var fs    = require('fs');
var path  = require('path');
var chalk = require('chalk');
var RSVP  = require('rsvp');

var denodeify = RSVP.denodeify;
var readFile  = denodeify(fs.readFile);
var writeFile = denodeify(fs.writeFile);

module.exports = {
  description: 'Blueprint for Ember NW.js projects',

  normalizeEntityName: function(entityName) {
    return entityName;
  },

  afterInstall: function(options) {
    var _this = this;
    var dependencies = this.project.dependencies();

    return this.addNwConfig(options)
      .then(function() {
        if (!dependencies.nw) {
          return _this.addPackageToProject('nw');
        }
      });
  },

  addNwConfig: function(options) {
    var ui = this.ui;
    var project = this.project;

    if (project.pkg.main) {
      return RSVP.resolve();
    }

    var packageJsonPath = path.join(project.root, 'package.json');
    var promise = readFile(packageJsonPath, { encoding: 'utf8' });

    return promise.then(function(data) {
      var json = JSON.parse(data);
      json.main = 'dist/index.html';
      json.window = { width: 960, height: 600 };
      ui.writeLine('  ' + chalk.yellow('overwrite') + ' package.json');

      if (!options.dryRun) {
        return writeFile(packageJsonPath, JSON.stringify(json, null, '  '));
      }
    });
  }
};


<|start_filename|>lib/commands/nw.js<|end_filename|>
'use strict';

var spawn = require('child_process').spawn;
var chalk = require('chalk');
var RSVP  = require('rsvp');

var Promise = RSVP.Promise;

module.exports = {
  name: 'nw',
  description: 'Builds your app and launches NW.js',

  init: function() {
    this._super.init && this._super.init.apply(this, arguments);
    process.env.EMBER_CLI_NW = true;
  },

  availableOptions: [
    { name: 'environment', type: String, default: 'development', aliases: ['e', { 'dev': 'development' }, { 'prod': 'production' }] },
    { name: 'output-path', type: String, default: 'dist/', aliases: ['o'] }
  ],

  buildWatch: function(options) {
    var ui = this.ui;
    var Watcher = require('ember-cli/lib/models/watcher');
    var Builder = require('ember-cli/lib/models/builder');

    ui.startProgress(chalk.green('Building'), chalk.green('.'));

    var watcher = new Watcher({
      ui: ui,
      builder: new Builder({
        ui: ui,
        project: this.project,
        environment: options.environment,
        outputPath: options.outputPath
      }),
      analytics: this.analytics,
      options: options
    });

    return watcher;
  },

  runNW: function() {
    var ui = this.ui;
    var project = this.project;

    return new Promise(function(resolve, reject) {
      ui.writeLine(chalk.green('Starting nw.js...'));

      var findNW = require('../helpers/find-nw');
      var nwCommand = findNW(project);

      var child = spawn(nwCommand, ['.'], { cwd: project.root, stdio: 'inherit' });

      child.on('error', function(error) {
        if (error.code === 'ENOENT') {
          ui.writeLine('');
          ui.writeLine(chalk.red("Error running the following command: " + nwCommand));
          ui.writeLine('');
          ui.writeLine(chalk.yellow("Either re-run the blueprint with 'ember g ember-cli-nwjs' to add NW.js as an NPM dependency in your project,"));
          ui.writeLine(chalk.yellow("or set an environment variable named 'NW_PATH' pointing to your NW.js binary."));
          reject();
        } else {
          ui.writeLine(chalk.red('Error running nw.js.'));
          reject(error);
        }
      });

      child.on('exit', function() {
        ui.writeLine(chalk.green('nw.js exited.'));
        resolve();
      });
    });
  },

  run: function(options) {
    var _this = this;

    return this.buildWatch(options)
      .then(function() {
        return _this.runNW();
      });
  }
};


<|start_filename|>tests/fixtures/project-with-config/config/nw-package.js<|end_filename|>
module.exports = {
  buildDir: 'build',
  cacheDir: 'cache',
  files: ['package.json'],
  platforms: ['osx64', 'win64'],
  appName: 'foo',
  appVersion: '0.0.1'
};


<|start_filename|>tests/unit/commands/nw-test/index-test.js<|end_filename|>
'use strict';

var fs            = require('fs');
var path          = require('path');
var RSVP          = require('rsvp');
var sinon         = require('sinon');
var Command       = require('ember-cli/lib/models/command');
var Task          = require('ember-cli/lib/models/task');
var TestTask      = require('ember-cli/lib/tasks/test');
var MockUI        = require('ember-cli/tests/helpers/mock-ui');
var MockAnalytics = require('ember-cli/tests/helpers/mock-analytics');
var MockProject   = require('../../../helpers/mocks/project');
var expect        = require('../../../helpers/expect');

var Command = require('ember-cli/lib/models/command');

describe('ember nw:test command', function() {
  var CommandUnderTest, commandOptions, tasks, outputPath;

  beforeEach(function() {
    var cmd = require('../../../../lib/commands/nw-test');
    CommandUnderTest = Command.extend(cmd);

    commandOptions = {
      ui: new MockUI(),
      analytics: new MockAnalytics(),
      settings: {},
      project: new MockProject('project-with-test-config'),

      tasks: {
        Build: Task.extend({
          run: function(options) {
            outputPath = options.outputPath;

            var fixturePath = path.join(__dirname, '../../../fixtures');
            var baseIndex = path.join(fixturePath, 'project-with-test-config', 'tests', 'index.html');

            var testsDir = path.join(outputPath, 'tests');
            var testsIndex = path.join(testsDir, 'index.html');

            fs.mkdirSync(testsDir, 0x1ff);

            var indexContent = fs.readFileSync(baseIndex, { encoding: 'utf8' });
            fs.writeFileSync(testsIndex, indexContent);

            tasks.push('build');
            return RSVP.resolve();
          }
        }),

        Test: Task.extend({
          run: function(options) {
            tasks.push('test');
            return RSVP.resolve();
          }
        })
      }
    };

    tasks = [];
  });

  it('should build the project before running tests', function() {
    var command = new CommandUnderTest(commandOptions).validateAndRun();

    return expect(command).to.be.fulfilled
      .then(function() {
        expect(tasks).to.deep.equal(['build', 'test']);
      });
  });

  it('should call the Test task with the correct options', function() {
    var testem, testOptions;

    commandOptions.tasks.Test = TestTask.extend({
      init: function(options) {
        var Testem = require('testem');
        testem = this.testem = new Testem();

        sinon.stub(this.testem, 'startCI', function(options, callback) {
          this.app = {
            reporter: { total: 10 }
          };
          testOptions = options;
          callback();
        });
      }
    });

    var command = new CommandUnderTest(commandOptions).validateAndRun();

    return expect(command).to.be.fulfilled
      .then(function() {
        var ciLauncherName = 'NW.js (CI)';
        var devLauncherName = 'NW.js';

        expect(testOptions.cwd).to.equal(outputPath);
        expect(testOptions['launch_in_ci']).to.deep.equal([ciLauncherName]);
        expect(testOptions['launch_in_dev']).to.deep.equal([devLauncherName]);

        var ciLauncher = testOptions.launchers[ciLauncherName] || {};
        expect(ciLauncher.protocol).to.equal('tap');

        var runnerPath = require.resolve('../../../../lib/commands/nw-test/runner');
        expect(ciLauncher.command).to.equal('node "' + runnerPath + '" --nw-path="nw" --tests-path="' + path.join(outputPath, 'tests') + '"');

        var devLauncher = testOptions.launchers[devLauncherName] || {};
        expect(devLauncher.protocol).to.equal('browser');
        expect(devLauncher.command).to.equal('node "' + runnerPath + '" --nw-path="nw" --tests-path="' + path.join(outputPath, 'tests') + '"');

        testem.startCI.restore();
      });
  });
});


<|start_filename|>lib/commands/nw-test/reporter.js<|end_filename|>
'use strict';

var util = require('util');
var TapReporter = require('testem/lib/reporters/tap_reporter');

function CustomTapReporter() {
  this.out = process.stdout;
  this.silent = false;
  this.stoppedOnError = null;
  this.id = 1;
  this.total = 0;
  this.pass = 0;
  this.skipped = 0;
  this.results = [];
  this.errors = [];
  this.logs = [];
}

util.inherits(CustomTapReporter, TapReporter);

CustomTapReporter.prototype.report = function(prefix, data) {
  var stack = data.items && data.items.length > 0 && data.items[0].stack;
  if (stack) {
    try {
      var details = JSON.parse(stack.replace(/^#\s*/, ''));
      data.error = {
        actual: details.actual,
        expected: details.expected,
        message: details.message
      };
    } catch (error) {
      process.stderr.write(stack);
    }
  }

  CustomTapReporter.super_.prototype.report.call(this, prefix, data);
};

module.exports = new CustomTapReporter();


<|start_filename|>lib/resources/shim-head.js<|end_filename|>
/* jshint browser: true */
(function(window) {
  if (!window.nwDispatcher && !window.nw) { return; }

  // Delete some variables that mislead third-party libraries into thinking
  // that the running environment is Node.
  delete global.window;   // can be accessed via `window`
  delete window.process;  // can be accessed via `global.process`

  // Rename node's `require` to avoid conflicts with AMD's `require`.
  window.requireNode = window.require;
  delete window.require;
})(this);


<|start_filename|>lib/helpers/find-nw.js<|end_filename|>
'use strict';

var fs   = require('fs');
var path = require('path');
var os   = require('os');

function getLocalNW(nwLocalPath) {
  var nw;
  var platform = os.platform();

  if (platform === 'darwin') {
    if (fs.existsSync(path.join(nwLocalPath, 'Contents'))) {
      nw = path.join(nwLocalPath, 'Contents', 'MacOS', 'nwjs');
    } else {
      nw = path.join(nwLocalPath, 'nwjs.app', 'Contents', 'MacOS', 'nwjs');
    }
  } else if (platform === 'win32') {
    nw = path.join(nwLocalPath, 'nw.exe');
  } else {
    nw = path.join(nwLocalPath, 'nw');
  }

  return nw;
}

module.exports = function(project) {
  var nw;

  var nwLocalPath = path.resolve(project.root, 'node_modules/nw/nwjs');
  if (fs.existsSync(nwLocalPath)) {
    nw = getLocalNW(nwLocalPath);
  } else if (process.env.NW_PATH) {
    nw = process.env.NW_PATH;
  } else {
    nw = 'nw';
  }

  return nw;
};


<|start_filename|>tests/unit/helpers/find-nw-test.js<|end_filename|>
'use strict';

var path    = require('path');
var mockery = require('mockery');
var expect  = require('../../helpers/expect');

describe("The command to start NW.js", function() {
  var fixturePath;

  before(function() {
    fixturePath = path.resolve(__dirname, '..', '..', 'fixtures');
  });

  describe("when the `nw` npm package is installed", function() {
    before(function() {
      mockery.enable({
        useCleanCache: true,
        warnOnUnregistered: false
      });
    });

    after(function() {
      mockery.disable();
    });

    describe("and the platform is Mac", function() {
      var findNW;

      before(function() {
        mockery.registerMock('os', {
          platform: function() {
            return 'darwin';
          }
        });

        findNW = require('../../../lib/helpers/find-nw');
      });

      after(function() {
        mockery.deregisterMock('os');
        mockery.resetCache();
      });

      it("should be `node_modules/nw/nwjs/nwjs.app/Contents/MacOS/nwjs`", function() {
        var project = {
          root: path.join(fixturePath, 'project-darwin-1')
        };

        var nw = findNW(project);
        var expectedNW = ['node_modules', 'nw', 'nwjs', 'nwjs.app', 'Contents', 'MacOS', 'nwjs'].join(path.sep);

        expect(nw).to.contain(expectedNW);
      });

      it("should be `node_modules/nw/nwjs/Contents/MacOS/nwjs`", function() {
        var project = {
          root: path.join(fixturePath, 'project-darwin-2')
        };

        var nw = findNW(project);
        var expectedNW = ['node_modules', 'nw', 'nwjs', 'Contents', 'MacOS', 'nwjs'].join(path.sep);

        expect(nw).to.contain(expectedNW);
      });
    });

    describe("and the platform is Windows", function() {
      var findNW;

      before(function() {
        mockery.registerMock('os', {
          platform: function() {
            return 'win32';
          }
        });

        findNW = require('../../../lib/helpers/find-nw');
      });

      after(function() {
        mockery.deregisterMock('os');
        mockery.resetCache();
      });

      it("should be `node_modules/nw/nwjs/nw.exe`", function() {
        var project = {
          root: path.join(fixturePath, 'project-win32')
        };

        var nw = findNW(project);
        var expectedNW = ['node_modules', 'nw', 'nwjs', 'nw.exe'].join(path.sep);

        expect(nw).to.contain(expectedNW);
      });
    });

    describe("and the platform is not Mac or Windows", function() {
      var findNW;

      before(function() {
        mockery.registerMock('os', {
          platform: function() {
            return 'linux';
          }
        });

        findNW = require('../../../lib/helpers/find-nw');
      });

      after(function() {
        mockery.deregisterMock('os');
        mockery.resetCache();
      });

      it("should be  `node_modules/nw/nwjs/nw`", function() {
        var project = {
          root: path.join(fixturePath, 'project-linux')
        };

        var nw = findNW(project);
        var expectedNW = ['node_modules', 'nw', 'nwjs', 'nw'].join(path.sep);

        expect(nw).to.contain(expectedNW);
      });
    });
  });

  describe("when the `nw` npm package is not installed", function() {
    var findNW, _envNW;

    before(function() {
      findNW = require('../../../lib/helpers/find-nw');
    });

    beforeEach(function() {
      _envNW = process.env.NW_PATH;
    });

    afterEach(function() {
      process.env.NW_PATH = _envNW;
    });

    describe("and the `NW_PATH` environment variable is set", function() {
      it("should be the value of `NW_PATH`", function() {
        var project = {
          root: path.join(fixturePath, 'project-empty')
        };

        var envNW = '/custom/path/to/nw';
        process.env.NW_PATH = envNW;

        var nw = findNW(project);
        expect(nw).to.equal(envNW);
      });
    });

    describe("and the `NW_PATH` environment variable is not set", function() {
      it("should be `nw`", function() {
        var project = {
          root: path.join(fixturePath, 'project-empty')
        };

        delete process.env.NW_PATH;

        var nw = findNW(project);
        expect(nw).to.equal('nw');
      });
    });
  });
});


<|start_filename|>lib/commands/nw-test/task.js<|end_filename|>
'use strict';

var path = require('path');
var findNW = require('../../helpers/find-nw');

function safePath(filePath) {
  // Guard against file paths that contain spaces
  return '"' + filePath + '"';
}

module.exports = {
  testemOptions: function(options) {
    /* jscs:disable requireCamelCaseOrUpperCaseIdentifiers */
    return {
      host: options.host,
      port: options.port,
      cwd: options.outputPath,
      reporter: options.reporter,
      middleware: this.addonMiddlewares(),
      launch_in_dev: ['NW.js'],
      launch_in_ci: ['NW.js (CI)'],
      launchers: {
        'NW.js': {
          command: this.nwCommand(options),
          protocol: 'browser'
        },
        'NW.js (CI)': {
          command: this.nwCommand(options),
          protocol: 'tap'
        }
      }
    };
    /* jscs:enable requireCamelCaseOrUpperCaseIdentifiers */
  },

  nwCommand: function(options) {
    var command = 'node';
    var commandArgs = safePath(path.join(__dirname, './runner.js'));
    var commandFlags = [
      '--nw-path=' + safePath(findNW(this.project)),
      '--tests-path=' + safePath(path.join(options.outputPath, 'tests'))
    ];

    return [command, commandArgs].concat(commandFlags).join(' ');
  }
};


<|start_filename|>tests/unit/commands/nw-test/reporter-test.js<|end_filename|>
'use strict';

var sinon  = require('sinon');
var expect = require('../../../helpers/expect');

describe('CustomTapReporter', function() {
  var tapReporter, logs;

  before(function() {
    tapReporter = require('../../../../lib/commands/nw-test/reporter');
  });

  beforeEach(function() {
    logs = [];

    sinon.stub(process.stdout, 'write', function(data) {
      logs.push(data);
    });
  });

  describe('when a test passes', function() {
    it('should not include error details', function() {
      var data = {
        passed: 1,
        failed: 0,
        total: 1,
        id: 48,
        name: 'Unit - Reporter - successful test',
        items: []
      };

      tapReporter.report('NW.js', data);
      process.stdout.write.restore();

      expect(logs).to.deep.equal([
        'ok 1 NW.js - Unit - Reporter - successful test\n'
      ]);
    });
  });

  describe('when a test fails', function() {
    it('should include error details', function() {
      var stack = {
        module: 'Unit - Reporter',
        name: 'failed test',
        result: false,
        message: 'foo is true',
        actual: false,
        expected: true,
        testId: '593a7efd',
        runtime: 222,
        source: '    at file:///Users/user/project/foo/bar.js:45737:33)'
      };

      var data = {
        passed: 0,
        failed: 1,
        total: 1,
        id: 23,
        name: 'Unit - Reporter - failed test',
        items: [{
          id: 1,
          ok: false,
          name: 'Unit - Reporter - failed test',
          stack: JSON.stringify(stack),
          passed: false
        }]
      };

      tapReporter.report('NW.js', data);
      process.stdout.write.restore();

      expect(logs).to.deep.equal([
        'not ok 2 NW.js - Unit - Reporter - failed test\n' +
        '    ---\n' +
        '        actual: >\n' +
        '            false\n' +
        '        expected: >\n' +
        '            true\n' +
        '        message: >\n' +
        '            foo is true\n' +
        '    ...\n'
      ]);
    });
  });
});


<|start_filename|>tests/helpers/mocks/project.js<|end_filename|>
'use strict';

var path = require('path');

function MockProject(name, pkg) {
  this.name = name;
  this.pkg = pkg || {};
  this.root = path.resolve(__dirname, '..', '..', 'fixtures', name);
  this.addons = [];
}

module.exports = MockProject;

MockProject.prototype.isEmberCLIProject = function() {
  return true;
};

MockProject.prototype.initializeAddons = function() {};

MockProject.prototype.hasDependencies = function() {
  return true;
};


<|start_filename|>tests/mocha-jscs-test.js<|end_filename|>
var mochaJSCS = require('mocha-jscs');

mochaJSCS(['lib', 'blueprints']);


<|start_filename|>lib/commands/nw-test/index.js<|end_filename|>
'use strict';

var Builder = require('ember-cli/lib/models/builder');
var Watcher = require('ember-cli/lib/models/watcher');

module.exports = {
  name: 'nw:test',
  description: 'Runs your test suite in NW.js',

  availableOptions: [
    { name: 'environment', type: String, default: 'test', aliases: ['e'] },
    { name: 'server', type: Boolean, description: 'Run tests in interactive mode', default: false },
    { name: 'protocol', type: 'String', description: 'The protocol to use when running with --server', default: 'http' },
    { name: 'host', type: String, description: 'The host to use when running with --server', default: 'localhost', aliases: ['H'] },
    { name: 'port', type: Number, description: 'The port to use when running with --server', default: 7357, aliases: ['p'] },
    { name: 'module', type: String,  description: 'The name of a test module to run', aliases: ['m'] },
    { name: 'filter', type: String,  description: 'A string to filter tests to run', aliases: ['f'] }
  ],

  init: function() {
    this._super.init && this._super.init.apply(this, arguments);
    this.assign    = require('lodash/object').assign;
    this.quickTemp = require('quick-temp');

    this.Builder = this.Builder || Builder;
    this.Watcher = this.Watcher || Watcher;

    if (!this.testing) {
      process.env.EMBER_CLI_TEST_COMMAND = true;
    }

    process.env.EMBER_CLI_NW = true;
  },

  tmp: function() {
    return this.quickTemp.makeOrRemake(this, '-testsDistNW');
  },

  rmTmp: function() {
    this.quickTemp.remove(this, '-testsDistNW');
  },

  taskOptions: function() {
    return {
      ui: this.ui,
      analytics: this.analytics,
      project: this.project
    };
  },

  runTestsForCI: function(options) {
    var buildTask = new this.tasks.Build(this.taskOptions());

    var NWTest = this.tasks.Test.extend(require('./task'));
    var testTask = new NWTest(this.taskOptions());

    // Test filtering
    var queryString = this.getTestPageQueryString(options);
    if (queryString.length > 0) {
      process.env.NW_TEST_PAGE_OPTIONS = queryString;
    }

    var testOptions = this.assign({}, options, {
      outputPath: options.outputPath,
      reporter: require('./reporter')
    });

    return buildTask.run({
        environment: options.environment,
        outputPath: options.outputPath
      })
      .then(function() {
        return testTask.run(testOptions);
      });
  },

  runTestsForDev: function(options) {
    process.env.NW_TESTS_DEV = true;
    process.env.NW_TESTEM_SERVER_URL = options.protocol + '://' + options.host + ':' + options.port;

    // Test filtering
    var queryString = this.getTestPageQueryString(options);
    if (queryString.length > 0) {
      process.env.NW_TEST_PAGE_OPTIONS = queryString;
    }

    // Start the test server task
    var testOptions = this.assign({}, options, {
      outputPath: options.outputPath,
      project: this.project
    });

    var taskOptions = this.taskOptions();
    taskOptions.builder = new this.Builder(testOptions);

    var NWTestServer = this.tasks.TestServer.extend(require('./task'));
    var testServer = new NWTestServer(taskOptions);

    testOptions.watcher = new this.Watcher(this.assign(taskOptions, {
      verbose: false,
      options: options
    }));

    return testServer.run(testOptions);
  },

  getTestPageQueryString: function(options) {
    var params = [];

    if (options.module) {
      params.push('module=' + options.module);
    }

    if (options.filter) {
      params.push('filter=' + options.filter.toLowerCase());
    }

    return params.join('&');
  },

  run: function(options) {
    options.outputPath = this.tmp();

    var promise;

    if (options.server) {
      // server mode will build continuously
      promise = this.runTestsForDev(options);
    } else {
      promise = this.runTestsForCI(options);
    }

    return promise.finally(this.rmTmp.bind(this));
  }
};
