var ConfigParser = require('../configParser'),
  q = require('q');

/**
 * Execute the Runner's test cases through Cucumber.
 *
 * @param {Runner} runner The current Protractor Runner.
 * @param {Array} specs Array of Directory Path Strings.
 * @return {q.Promise} Promise resolved with the test results
 */
exports.run = function(runner, specs) {
  // TODO - add the event interface for cucumber.
  var Cucumber = require('cucumber'),
      execOptions = ['node', 'node_modules/.bin/cucumber-js'],
      cucumberResolvedRequire;

  // Set up exec options for Cucumber
  execOptions = execOptions.concat(specs);
  if (runner.getConfig().cucumberOpts) {

    // Process Cucumber Require param
    if (runner.getConfig().cucumberOpts.require) {
      // TODO - this should move into the launcher.
      cucumberResolvedRequire =
          ConfigParser.resolveFilePatterns(
              runner.getConfig().cucumberOpts.require,
              false,
              runner.getConfig().configDir);
      if (cucumberResolvedRequire && cucumberResolvedRequire.length) {
          execOptions = cucumberResolvedRequire.reduce(function(a, fn) {
            return a.concat('-r', fn);
          }, execOptions);
      }
    }

    // Process Cucumber Tag param
    if (Array.isArray(runner.getConfig().cucumberOpts.tags)) {
        for (var i in runner.getConfig().cucumberOpts.tags) {
            var tags = runner.getConfig().cucumberOpts.tags[i];
            execOptions.push('-t');
            execOptions.push(tags);
        }
    } else if (runner.getConfig().cucumberOpts.tags) {
      execOptions.push('-t');
      execOptions.push(runner.getConfig().cucumberOpts.tags);
    }

    // Process Cucumber Format param
    if (runner.getConfig().cucumberOpts.format) {
      execOptions.push('-f');
      execOptions.push(runner.getConfig().cucumberOpts.format);
    }

    // Process Cucumber 'coffee' param
    if (runner.getConfig().cucumberOpts.coffee) {
      execOptions.push('--coffee');
    }

    // Process Cucumber 'no-snippets' param
    if (runner.getConfig().cucumberOpts.noSnippets) {
      execOptions.push('--no-snippets');
    }
  }
  global.cucumber = Cucumber.Cli(execOptions);

  var testResult = [];
  var stepResults = {
    description: null,
    assertions: [],
    duration: 0
  };
  var scenarioFailed = false;

  var failedCount = 0;
  // Add a listener into cucumber so that protractor can find out which
  // steps passed/failed
  var addResultListener = function(formatter) {
    var feature = { getName: function() { return ''; } };
    var originalHandleBeforeFeatureEvent = formatter.handleBeforeFeatureEvent;
    formatter.handleBeforeFeatureEvent = function(event, callback) {
      feature = event.getPayloadItem('feature');
      if (typeof originalHandleBeforeFeatureEvent == 'function') {
        originalHandleBeforeFeatureEvent.apply(formatter, arguments);
      } else {
        callback();
      }
    };
    var originalHandleAfterScenarioEvent = formatter.handleAfterScenarioEvent;
    formatter.handleAfterScenarioEvent = function(event, callback) {
      var scenarioInfo = {
        name: event.getPayloadItem('scenario').getName(),
        category: feature.getName()
      };
      stepResults.description = scenarioInfo.name;
      if (scenarioFailed) {
        ++failedCount;
        runner.emit('testFail', scenarioInfo);
      } else {
        runner.emit('testPass', scenarioInfo);
      }

      testResult.push(stepResults);
      stepResults = {
        description: null,
        assertions: [],
        duration: 0
      };
      scenarioFailed = false;

      if (originalHandleAfterScenarioEvent
          && typeof(originalHandleAfterScenarioEvent) === 'function') {
            originalHandleAfterScenarioEvent(event, callback);
      } else {
        callback();
      }
    };

    var originalHandleStepResultEvent = formatter.handleStepResultEvent;
    formatter.handleStepResultEvent = function(event, callback) {
      var stepResult = event.getPayloadItem('stepResult');
      var step = stepResult.getStep();
      if (stepResult.isSuccessful()) {
        stepResults.assertions.push({
          description: step.getName(),
          passed: true
        });
        stepResults.duration += stepResult.getDuration();
      } else if (stepResult.isFailed()) {
        scenarioFailed = true;
        var failureMessage = stepResult.getFailureException();
        stepResults.assertions.push({
          description: step.getName(),
          passed: false,
          errorMsg: failureMessage.message,
          stackTrace: failureMessage.stack
        });
        stepResults.duration += stepResult.getDuration();
      }

      if (originalHandleStepResultEvent
          && typeof(originalHandleStepResultEvent) === 'function') {
            originalHandleStepResultEvent(event, callback);
      } else {
        callback();
      }
    };
  };

  return runner.runTestPreparer().then(function() {
    return q.promise(function(resolve, reject) {
      var cucumberConf = Cucumber.Cli.Configuration(execOptions);
      var runtime = Cucumber.Runtime(cucumberConf);
      var formatter = cucumberConf.getFormatter();
      addResultListener(formatter);
      runtime.attachListener(formatter);
      runtime.start(function() {
        try {
          if (runner.getConfig().onComplete) {
            runner.getConfig().onComplete();
          }
          resolve({
            failedCount: failedCount,
            specResults: testResult
          });
        } catch (err) {
          reject(err);
        }
      });
    });
  });
};

@@ -4138,32 +4138,77 @@
 sertions.push(%7B%0A
+          keyword: step.getKeyword().trim(),%0A
           descri
@@ -4489,32 +4489,77 @@
 sertions.push(%7B%0A
+          keyword: step.getKeyword().trim(),%0A
           descri

add keyword