Repository: opentable/oc (js)

Issue:
Running local instance of registry via node does not create _package folders
**Background** : We are working on enabling developers to be able to write integration test suites for their components, to do this one of the things needed is to be able to bring up and stop a local instance of the registry.

**Code** : 
```
startRegistry: function(done) {
            registry = new oc.Registry({
                baseUrl: 'http://localhost:3030/',
                local: true,
                port: 3030,
                hotReloading: false,
                path: path.resolve('../'),
                env: {name: 'local'},
                verbosity: 1,
                discovery: true
            });
            registry.start(done);
        }
```
Pretty straight forward snippet. Note that we need discovery true so that we can use phantomJS or nightmare js or casper ... to be able to load the component in preview mode.

**Problem** : So the registry did start up but it does not generate the _package folder for the component, which means we cannot really load the component in preview mode.

Thoughts?

Pull request description:
[DX-218] Expose cli to be used programmatically
Fixes #441 by exposing the dev interface and allowing acceptance testing in a cleaner way

Patch:
diff --git a/src/cli/programmatic-api.js b/src/cli/programmatic-api.js
new file mode 100644
index 00000000..48f3efcf
--- /dev/null
+++ b/src/cli/programmatic-api.js
@@ -0,0 +1,35 @@
+'use strict';
+
+const _ = require('lodash');
+const Local = require('./domain/local');
+
+function wrap(cmdScript, withRegistry) {
+  return function(options, cb) {
+    const dependencies = {
+      local: new Local(),
+      logger: options.logger || { log() {}, err() {}, ok() {}, warn() {} }
+    };
+
+    if (withRegistry) {
+      const Registry = require('./domain/registry');
+      dependencies.registry = new Registry({ registry: options.registry });
+    }
+
+    const opts = _.omit(options, 'logger', 'registry');
+    require(`./facade/${cmdScript}`)(dependencies)(opts, cb);
+  };
+}
+
+module.exports = {
+  dev: wrap('dev'),
+  init: wrap('init'),
+  mock: wrap('mock'),
+  package: wrap('package'),
+  publish: wrap('publish', true),
+  preview: wrap('preview', true),
+  registry: {
+    add: wrap('registry-add', true),
+    ls: wrap('registry-ls', true),
+    remove: wrap('registry-remove', true)
+  }
+};
diff --git a/src/index.js b/src/index.js
index 293d61a4..3fddec97 100644
--- a/src/index.js
+++ b/src/index.js
@@ -1,4 +1,5 @@
 'use strict';
 
+module.exports.cli = require('./cli/programmatic-api');
 module.exports.Client = require('oc-client');
 module.exports.Registry = require('./registry');


Test patch:
diff --git a/test/unit/cli-programmatic-api.js b/test/unit/cli-programmatic-api.js
new file mode 100644
index 00000000..3d6da818
--- /dev/null
+++ b/test/unit/cli-programmatic-api.js
@@ -0,0 +1,71 @@
+'use strict';
+
+const _ = require('lodash');
+const expect = require('chai').expect;
+const injectr = require('injectr');
+const sinon = require('sinon');
+
+describe('cli : programmatic-api', () => {
+  const deps = {
+    './facade/dev': sinon.stub().returns(() => {}),
+    './facade/init': sinon.stub().returns(() => {}),
+    './facade/mock': sinon.stub().returns(() => {}),
+    './facade/package': sinon.stub().returns(() => {}),
+    './facade/publish': sinon.stub().returns(() => {}),
+    './facade/preview': sinon.stub().returns(() => {}),
+    './facade/registry-add': sinon.stub().returns(() => {}),
+    './facade/registry-ls': sinon.stub().returns(() => {}),
+    './facade/registry-remove': sinon.stub().returns(() => {})
+  };
+  const programmaticApi = injectr('../../src/cli/programmatic-api.js', deps);
+
+  const scenarios = [
+    { cmd: 'dev', facade: 'dev', dependencies: ['local', 'logger'] },
+    { cmd: 'init', facade: 'init', dependencies: ['local', 'logger'] },
+    { cmd: 'mock', facade: 'mock', dependencies: ['local', 'logger'] },
+    { cmd: 'package', facade: 'package', dependencies: ['local', 'logger'] },
+    {
+      cmd: 'publish',
+      facade: 'publish',
+      dependencies: ['local', 'logger', 'registry']
+    },
+    {
+      cmd: 'preview',
+      facade: 'preview',
+      dependencies: ['local', 'logger', 'registry']
+    },
+    {
+      cmd: 'registry.add',
+      facade: 'registry-add',
+      dependencies: ['local', 'logger', 'registry']
+    },
+    {
+      cmd: 'registry.ls',
+      facade: 'registry-ls',
+      dependencies: ['local', 'logger', 'registry']
+    },
+    {
+      cmd: 'registry.remove',
+      facade: 'registry-remove',
+      dependencies: ['local', 'logger', 'registry']
+    }
+  ];
+
+  scenarios.forEach(scenario => {
+    describe(`cmd: cli.${scenario.cmd}`, () => {
+      const fn = _.get(programmaticApi, scenario.cmd);
+
+      it(`should be function`, () => {
+        expect(typeof fn).to.equal('function');
+        expect(fn.length).to.equal(2);
+      });
+
+      it(`should correctly wrap the related facade`, () => {
+        fn({}, () => {});
+        expect(deps[`./facade/${scenario.facade}`].args[0][0])
+          .to.be.an('object')
+          .that.has.all.keys(scenario.dependencies);
+      });
+    });
+  });
+});