whole_func_string: function Reporter(options) {
    var _defaultOutputFile = path.resolve(process.cwd(), './_test-output', 'protractor-results.txt');
    options.outputFile = options.outputFile || _defaultOutputFile;
  
    initOutputFile(options.outputFile);
    options.appDir = options.appDir || './';
    var _root = { appDir: options.appDir, suites: [] };
    log('AppDir: ' + options.appDir, +1);
    var _currentSuite;
  
    this.suiteStarted = function (suite) {
      _currentSuite = { description: suite.description, status: null, specs: [] };
      _root.suites.push(_currentSuite);
      log('Suite: ' + suite.description, +1);
    };
  
    this.suiteDone = function (suite) {
      var statuses = _currentSuite.specs.map(function (spec) {
        return spec.status;
      });
      statuses = _.uniq(statuses);
      var status = statuses.indexOf('failed') >= 0 ? 'failed' : statuses.join(', ');
      _currentSuite.status = status;
      log('Suite ' + _currentSuite.status + ': ' + suite.description, -1);
    };
  
    this.specStarted = function (spec) {
  
    };
  
    this.specDone = function (spec) {
      var currentSpec = {
        description: spec.description,
        status: spec.status
      };
      if (spec.failedExpectations.length > 0) {
        currentSpec.failedExpectations = spec.failedExpectations;
  
        // Take screenshot in case of failure
        browser.takeScreenshot().then(function (png) {
          const fileName = path.resolve(process.cwd(), '_test-output', `${spec.fullName.replace(/\s/g, "-")}.png`);
          console.log(`Saving ${fileName}`);
          var stream = fs.createWriteStream(fileName);
          stream.write(new Buffer(png, 'base64'));
          stream.end();
        });
      }
  
      _currentSuite.specs.push(currentSpec);
      log(spec.status + ' - ' + spec.description);
    };
  
    this.jasmineDone = function () {
      outputFile = options.outputFile;
      //// Alternate approach - just stringify the _root - not as pretty
      //// but might be more useful for automation.
      // var output = JSON.stringify(_root, null, 2);
      var output = formatOutput(_root);
      fs.appendFileSync(outputFile, output);
    };
  
    function ensureDirectoryExistence(filePath) {
      var dirname = path.dirname(filePath);
      if (directoryExists(dirname)) {
        return true;
      }
      ensureDirectoryExistence(dirname);
      fs.mkdirSync(dirname);
    }
  
    function directoryExists(path) {
      try {
        return fs.statSync(path).isDirectory();
      }
      catch (err) {
        return false;
      }
    }
  
    function initOutputFile(outputFile) {
      ensureDirectoryExistence(outputFile);
      var header = "Protractor results for: " + (new Date()).toLocaleString() + "\n\n";
      fs.writeFileSync(outputFile, header);
    }
  
    // for output file output
    function formatOutput(output) {
      var indent = '  ';
      var pad = '  ';
      var results = [];
      results.push('AppDir:' + output.appDir);
      output.suites.forEach(function (suite) {
        results.push(pad + 'Suite: ' + suite.description + ' -- ' + suite.status);
        pad += indent;
        suite.specs.forEach(function (spec) {
          results.push(pad + spec.status + ' - ' + spec.description);
          if (spec.failedExpectations) {
            pad += indent;
            spec.failedExpectations.forEach(function (fe) {
              results.push(pad + 'message: ' + fe.message);
            });
            pad = pad.substr(2);
          }
        });
        pad = pad.substr(2);
        results.push('');
      });
      results.push('');
      return results.join('\n');
    }
  
    // for console output
    var _pad;
    function log(str, indent) {
      _pad = _pad || '';
      if (indent == -1) {
        _pad = _pad.substr(2);
      }
      console.log(_pad + str);
      if (indent == 1) {
        _pad = _pad + '  ';
      }
    }
  
  }
func_code_string: function Reporter(options) {
    var _defaultOutputFile = path.resolve(process.cwd(), './_test-output', 'protractor-results.txt');
    options.outputFile = options.outputFile || _defaultOutputFile;
  
    initOutputFile(options.outputFile);
    options.appDir = options.appDir || './';
    var _root = { appDir: options.appDir, suites: [] };
    log('AppDir: ' + options.appDir, +1);
    var _currentSuite;
  
    this.suiteStarted = function (suite) {
      _currentSuite = { description: suite.description, status: null, specs: [] };
      _root.suites.push(_currentSuite);
      log('Suite: ' + suite.description, +1);
    };
  
    this.suiteDone = function (suite) {
      var statuses = _currentSuite.specs.map(function (spec) {
        return spec.status;
      });
      statuses = _.uniq(statuses);
      var status = statuses.indexOf('failed') >= 0 ? 'failed' : statuses.join(', ');
      _currentSuite.status = status;
      log('Suite ' + _currentSuite.status + ': ' + suite.description, -1);
    };
  
    this.specStarted = function (spec) {
  
    };
  
    this.specDone = function (spec) {
      var currentSpec = {
        description: spec.description,
        status: spec.status
      };
      if (spec.failedExpectations.length > 0) {
        currentSpec.failedExpectations = spec.failedExpectations;
  
        // Take screenshot in case of failure
        browser.takeScreenshot().then(function (png) {
          const fileName = path.resolve(process.cwd(), '_test-output', `${spec.fullName.replace(/\s/g, "-")}.png`);
          console.log(`Saving ${fileName}`);
          var stream = fs.createWriteStream(fileName);
          stream.write(new Buffer(png, 'base64'));
          stream.end();
        });
      }
  
      _currentSuite.specs.push(currentSpec);
      log(spec.status + ' - ' + spec.description);
    };
  
    this.jasmineDone = function () {
      outputFile = options.outputFile;
      //// Alternate approach - just stringify the _root - not as pretty
      //// but might be more useful for automation.
      // var output = JSON.stringify(_root, null, 2);
      var output = formatOutput(_root);
      fs.appendFileSync(outputFile, output);
    };
  
    function ensureDirectoryExistence(filePath) {
      var dirname = path.dirname(filePath);
      if (directoryExists(dirname)) {
        return true;
      }
      ensureDirectoryExistence(dirname);
      fs.mkdirSync(dirname);
    }
  
    function directoryExists(path) {
      try {
        return fs.statSync(path).isDirectory();
      }
      catch (err) {
        return false;
      }
    }
  
    function initOutputFile(outputFile) {
      ensureDirectoryExistence(outputFile);
      var header = "Protractor results for: " + (new Date()).toLocaleString() + "\n\n";
      fs.writeFileSync(outputFile, header);
    }
  
    // for output file output
    function formatOutput(output) {
      var indent = '  ';
      var pad = '  ';
      var results = [];
      results.push('AppDir:' + output.appDir);
      output.suites.forEach(function (suite) {
        results.push(pad + 'Suite: ' + suite.description + ' -- ' + suite.status);
        pad += indent;
        suite.specs.forEach(function (spec) {
          results.push(pad + spec.status + ' - ' + spec.description);
          if (spec.failedExpectations) {
            pad += indent;
            spec.failedExpectations.forEach(function (fe) {
              results.push(pad + 'message: ' + fe.message);
            });
            pad = pad.substr(2);
          }
        });
        pad = pad.substr(2);
        results.push('');
      });
      results.push('');
      return results.join('\n');
    }
  
    // for console output
    var _pad;
    function log(str, indent) {
      _pad = _pad || '';
      if (indent == -1) {
        _pad = _pad.substr(2);
      }
      console.log(_pad + str);
      if (indent == 1) {
        _pad = _pad + '  ';
      }
    }
  
  }
func_documentation_string: Custom reporter