<commit_before>const logger = require('winston');
const changelog = require('changelog');
const stringify = require('json-stringify-pretty-compact');
const github = require('./api/github');
const handlebars = require('./helpers/handlebars');
const versionsHelper = require('./helpers/versions');
const packageJson = require('./helpers/package-json');
const npmApi = require('./api/npm');

let config = null;

module.exports = renovate;

// This function manages the queue per-package file
function renovate(repoName, packageFile, setConfig) {
  // Initialize globals
  config = Object.assign({}, setConfig);
  config.packageFile = packageFile;

  logger.info(`Processing ${repoName} ${packageFile}`);

  // Start the chain
  return github.getFileJson(packageFile)
    .then(checkforRenovateConfig)
    .then(checkIfEnabled)
    .then(contents => packageJson.extractDependencies(contents, config.depTypes))
    .then(filterIgnoredDependencies)
    .then(findUpgrades)
    .then(processUpgradesSequentially)
    .then(() => { // eslint-disable-line promise/always-return
      logger.info(`${repoName} ${packageFile} done`);
    })
    .catch((error) => {
      if (error !== 'renovate disabled') {
        logger.error(`renovate caught error: ${error}`);
      }
    });
}

function checkforRenovateConfig(packageContent) {
  if (packageContent.renovate) {
    logger.debug(`package.json>renovate config: ${stringify(packageContent.renovate)}`);
    Object.assign(config, packageContent.renovate);
    logger.debug(`Updated config: ${JSON.stringify(config)}`);
  }
  return packageContent;
}

// Rejects promise if config.enabled = false
function checkIfEnabled(packageContent) {
  if (!config.enabled) {
    logger.info('Config is disabled. Skipping');
    return Promise.reject('renovate disabled');
  }
  return packageContent;
}

// Remove any dependencies that are on the ignore list
function filterIgnoredDependencies(dependencies) {
  return dependencies.filter(dependency => config.ignoreDeps.indexOf(dependency.depName) === -1);
}

function findUpgrades(dependencies) {
  const allDependencyUpgrades = [];
  // We create an array of promises so that they can be executed in parallel
  const allDependencyPromises = dependencies.reduce((promises, dep) => promises.concat(
    npmApi.getDependency(dep.depName)
    .then(npmDependency =>
      versionsHelper.determineUpgrades(npmDependency, dep.currentVersion, config))
    .then((upgrades) => {
      if (upgrades.length > 0) {
        logger.verbose(`${dep.depName}: Upgrades = ${JSON.stringify(upgrades)}`);
        upgrades.forEach((upgrade) => {
          allDependencyUpgrades.push(Object.assign({}, dep, upgrade));
        });
      } else {
        logger.verbose(`${dep.depName}: No upgrades required`);
      }
      return Promise.resolve();
    })
    .catch((error) => {
      logger.error(`Error finding upgrades for ${dep.depName}: ${error}`);
    })), []);
  // Return the upgrade array once all Promises are complete
  return Promise.all(allDependencyPromises).then(() => allDependencyUpgrades);
}

function processUpgradesSequentially(upgrades) {
  if (Object.keys(upgrades).length) {
    logger.verbose('Processing upgrades');
  } else {
    logger.verbose('No upgrades to process');
  }
  logger.verbose(`All upgrades: ${JSON.stringify(upgrades)}`);
  // We are processing each upgrade sequentially for two major reasons:
  // 1. Reduce chances of GitHub API rate limiting
  // 2. Edge case collision of branch name, e.g. dependency also listed as dev dependency
  return upgrades.reduce(
    (promise, upgrade) => promise
      .then(() => updateDependency(upgrade)), Promise.resolve());
}

function updateDependency(upgrade) {
  // Expand upgrade params
  const depType = upgrade.depType;
  const depName = upgrade.depName;
  const newVersion = upgrade.newVersion;
  // Helpers for templating
  const params = Object.assign({}, upgrade);
  if (upgrade.upgradeType === 'pin') {
    params.isPin = true;
  } else if (upgrade.upgradeType === 'major') {
    params.isMajor = true;
  } else if (upgrade.upgradeType === 'minor') {
    params.isMinor = true;
  }
  // Use templates to generate strings
  const branchName = handlebars.transform(config.branchName, params);
  const commitMessage = handlebars.transform(config.commitMessage, params);
  const prTitle = handlebars.transform(config.prTitle, params);

  return checkForUnmergeablePr()
  .then(ensureBranch)
  .then(getChangelog)
  .then(log => ensurePr(log))
  .catch((error) => {
    if (error === 'PR exists') {
      logger.verbose(`Skipping ${depName} upgrade as matching closed PR already existed`);
    } else {
      logger.error(`Error updating dependency ${depName}: ${error}`);
    }
    // Don't throw here - we don't want to stop the other renovations
  });

  // Check if same PR already existed and skip if so
  // This allows users to close an unwanted upgrade PR and not worry about seeing it raised again
  function checkForClosedPr() {
    if (config.recreateClosed) {
      logger.debug(`${depName}: Skipping closed PR check`);
      return Promise.resolve();
    }
    logger.debug(`${depName}: Checking for closed PR`);
    return github.checkForClosedPr(branchName, prTitle).then((prExisted) => {
      logger.debug(`Closed PR existed: ${prExisted}`);
      if (prExisted) {
        return Promise.reject('PR exists');
      }
      return Promise.resolve();
    });
  }

  function getBranchPr() {
    return github.getBranchPr(branchName)
    .then((branchPr) => {
      if (branchPr) {
        logger.debug(`Found open PR for ${branchName}`);
        return github.getPr(branchPr.number);
      }
      logger.debug(`Didn't find open PR for ${branchName}`);
      return null;
    });
  }

  function deleteUnmergeablePr(pr) {
    return github.updatePr(pr.number, `${pr.title} (unmergeable)`)
      .then(() => github.deleteBranch(branchName));
  }

  function checkForUnmergeablePr() {
    return getBranchPr()
    .then((pr) => {
      if (pr) {
        logger.debug(`checkForUnmergeablePr found PR #${pr.number}`);
        if (pr.mergeable_state === 'dirty') {
          logger.debug(`Existing PR ${pr.number} is not mergeable`);
          if (pr.additions * pr.deletions === 1) {
            // No other changes except ours
            logger.verbose(`Deleting branch ${branchName}`);
            // Rename PR and then delete the branch
            return deleteUnmergeablePr(pr);
          }
          logger.verbose(`Not deleting branch ${branchName} because it has additional changes`);
        }
        return Promise.resolve();
      }
      // If no open PR exists then check for any closed one
      return checkForClosedPr();
    });
  }

  function ensureBranch() {
    return github.branchExists(branchName)
    .then((branchExists) => {
      if (branchExists) {
        return updateExistingBranch();
      }
      return createNewBranch();
    });
  }

  function getNewPackageJson(content) {
    return packageJson.setNewValue(
      content,
      depType,
      depName,
      newVersion);
  }

  function updateExistingBranch() {
    logger.debug(`Checking if branch ${branchName} needs updating`);
    return github.getFileContent(config.packageFile, branchName)
    .then((existingContent) => {
      const newContent = getNewPackageJson(existingContent);
      if (newContent === existingContent) {
        logger.debug(`Branch ${branchName} is already up-to-date`);
        return Promise.resolve();
      }
      logger.verbose(`Adding commit '${commitMessage}' to branch ${branchName}`);
      return github.commitFile(config.packageFile, newContent, commitMessage, branchName);
    })
    .then((commit) => {
      if (!commit) {
        return Promise.resolve();
      }
      return github.updateBranch(branchName, commit);
    });
  }

  function createNewBranch() {
    logger.verbose(`${depName}: creating new branch ${branchName}`);
    return github.getFileContent(config.packageFile)
    .then(getNewPackageJson)
    .then(newContent => github.commitFile(config.packageFile, newContent, commitMessage))
    .then(commit => github.createBranch(branchName, commit));
  }

  function getChangelog() {
    if (!upgrade.workingVersion || upgrade.workingVersion === upgrade.newVersion) {
      return Object.assign(upgrade, { changelog: '' });
    }
    const semverString = `>${upgrade.workingVersion} <=${upgrade.newVersion}`;
    logger.debug(`semverString: ${semverString}`);
    return changelog.generate(upgrade.depName, semverString)
      .then(changelog.markdown)
      .catch((error) => {
        logger.verbose(`getChangelog error: ${error}`);
      })
      .then((res) => {
        if (!res) {
          logger.verbose(`No changelog for ${depName}`);
          return '';
        }
        return `### Changelog\n${res}`;
      })
      .then(log => Object.assign(upgrade, { changelog: log }));
  }

  // Ensures that PR exists with matching title/body
  function ensurePr(upgradeWithChangeLog) {
    logger.debug('Ensuring PR');

    const prBody = handlebars.transform(config.prBody, upgradeWithChangeLog);

    return github.getBranchPr(branchName)
    .then(processExistingPr)
    .catch((error) => {
      logger.error(`${depName} failed to ensure PR: ${error}`);
    });

    // Create PR based on current state
    function createPr() {
      return github.createPr(branchName, prTitle, prBody).then((newPr) => {
        logger.info(`${depName}: Created PR #${newPr.number}`);
        return newPr.number;
      });
    }
    // Update PR based on current state
    function updatePr(existingPr) {
      logger.debug(`updatePr: ${existingPr.number}`);
      return github.updatePr(existingPr.number, prTitle, prBody).then(() => {
        logger.info(`${depName}: Updated PR #${existingPr.number}`);
        return Promise.resolve();
      });
    }
    // Process a returned PR
    function processExistingPr(existingPr) {
      if (!existingPr) {
        logger.debug(`Didn't find existing PR for branch ${branchName}`);
        // We need to create a new PR
        return createPr()
        .then(prNo => addLabels(prNo))
        .then(addAssignees);
      }
      // Check if existing PR needs updating
      logger.debug(`processExistingPr: ${existingPr.number}`);
      if (existingPr.title === prTitle && existingPr.body === prBody) {
        logger.verbose(`${depName}: PR #${existingPr.number} already up-to-date`);
        return Promise.resolve();
      }
      // PR must need updating
      return updatePr(existingPr);
    }

    // Add assignees to a PR
    function addAssignees(prNo) {
      if (config.assignees.length === 0) {
        logger.debug(`No assignees to add to ${prNo}`);
        return Promise.resolve();
      }
      return github.addAssignees(prNo, config.assignees);
    }

    // Add labels to a PR
    function addLabels(prNo) {
      if (config.labels.length === 0) {
        logger.debug(`No labels to add to ${prNo}`);
        return Promise.resolve(prNo);
      }
      return github.addLabels(prNo, config.labels)
      .then(() => prNo);
    }
  }
}
<commit_msg>Rebase PRs instead of recreate<commit_after>const logger = require('winston');
const changelog = require('changelog');
const stringify = require('json-stringify-pretty-compact');
const github = require('./api/github');
const handlebars = require('./helpers/handlebars');
const versionsHelper = require('./helpers/versions');
const packageJson = require('./helpers/package-json');
const npmApi = require('./api/npm');

let config = null;

module.exports = renovate;

// This function manages the queue per-package file
function renovate(repoName, packageFile, setConfig) {
  // Initialize globals
  config = Object.assign({}, setConfig);
  config.packageFile = packageFile;

  logger.info(`Processing ${repoName} ${packageFile}`);

  // Start the chain
  return github.getFileJson(packageFile)
    .then(checkforRenovateConfig)
    .then(checkIfEnabled)
    .then(contents => packageJson.extractDependencies(contents, config.depTypes))
    .then(filterIgnoredDependencies)
    .then(findUpgrades)
    .then(processUpgradesSequentially)
    .then(() => { // eslint-disable-line promise/always-return
      logger.info(`${repoName} ${packageFile} done`);
    })
    .catch((error) => {
      if (error !== 'renovate disabled') {
        logger.error(`renovate caught error: ${error}`);
      }
    });
}

function checkforRenovateConfig(packageContent) {
  if (packageContent.renovate) {
    logger.debug(`package.json>renovate config: ${stringify(packageContent.renovate)}`);
    Object.assign(config, packageContent.renovate);
    logger.debug(`Updated config: ${JSON.stringify(config)}`);
  }
  return packageContent;
}

// Rejects promise if config.enabled = false
function checkIfEnabled(packageContent) {
  if (!config.enabled) {
    logger.info('Config is disabled. Skipping');
    return Promise.reject('renovate disabled');
  }
  return packageContent;
}

// Remove any dependencies that are on the ignore list
function filterIgnoredDependencies(dependencies) {
  return dependencies.filter(dependency => config.ignoreDeps.indexOf(dependency.depName) === -1);
}

function findUpgrades(dependencies) {
  const allDependencyUpgrades = [];
  // We create an array of promises so that they can be executed in parallel
  const allDependencyPromises = dependencies.reduce((promises, dep) => promises.concat(
    npmApi.getDependency(dep.depName)
    .then(npmDependency =>
      versionsHelper.determineUpgrades(npmDependency, dep.currentVersion, config))
    .then((upgrades) => {
      if (upgrades.length > 0) {
        logger.verbose(`${dep.depName}: Upgrades = ${JSON.stringify(upgrades)}`);
        upgrades.forEach((upgrade) => {
          allDependencyUpgrades.push(Object.assign({}, dep, upgrade));
        });
      } else {
        logger.verbose(`${dep.depName}: No upgrades required`);
      }
      return Promise.resolve();
    })
    .catch((error) => {
      logger.error(`Error finding upgrades for ${dep.depName}: ${error}`);
    })), []);
  // Return the upgrade array once all Promises are complete
  return Promise.all(allDependencyPromises).then(() => allDependencyUpgrades);
}

function processUpgradesSequentially(upgrades) {
  if (Object.keys(upgrades).length) {
    logger.verbose('Processing upgrades');
  } else {
    logger.verbose('No upgrades to process');
  }
  logger.verbose(`All upgrades: ${JSON.stringify(upgrades)}`);
  // We are processing each upgrade sequentially for two major reasons:
  // 1. Reduce chances of GitHub API rate limiting
  // 2. Edge case collision of branch name, e.g. dependency also listed as dev dependency
  return upgrades.reduce(
    (promise, upgrade) => promise
      .then(() => updateDependency(upgrade)), Promise.resolve());
}

function updateDependency(upgrade) {
  // Expand upgrade params
  const depType = upgrade.depType;
  const depName = upgrade.depName;
  const newVersion = upgrade.newVersion;
  // Helpers for templating
  const params = Object.assign({}, upgrade);
  if (upgrade.upgradeType === 'pin') {
    params.isPin = true;
  } else if (upgrade.upgradeType === 'major') {
    params.isMajor = true;
  } else if (upgrade.upgradeType === 'minor') {
    params.isMinor = true;
  }
  // Use templates to generate strings
  const branchName = handlebars.transform(config.branchName, params);
  const commitMessage = handlebars.transform(config.commitMessage, params);
  const prTitle = handlebars.transform(config.prTitle, params);

  return checkForClosedPr()
  .then(ensureBranch)
  .then(getChangelog)
  .then(log => ensurePr(log))
  .catch((error) => {
    if (error === 'PR exists') {
      logger.verbose(`Skipping ${depName} upgrade as matching closed PR already existed`);
    } else {
      logger.error(`Error updating dependency ${depName}: ${error}`);
    }
    // Don't throw here - we don't want to stop the other renovations
  });

  // Check if same PR already existed and skip if so
  // This allows users to close an unwanted upgrade PR and not worry about seeing it raised again
  function checkForClosedPr() {
    if (config.recreateClosed) {
      logger.debug(`${depName}: Skipping closed PR check`);
      return Promise.resolve();
    }
    logger.debug(`${depName}: Checking for closed PR`);
    return github.checkForClosedPr(branchName, prTitle).then((prExisted) => {
      logger.debug(`Closed PR existed: ${prExisted}`);
      if (prExisted) {
        return Promise.reject('PR exists');
      }
      return Promise.resolve();
    });
  }

  function getBranchPr() {
    return github.getBranchPr(branchName)
    .then((branchPr) => {
      if (branchPr) {
        logger.debug(`Found open PR for ${branchName}`);
        return github.getPr(branchPr.number);
      }
      logger.debug(`Didn't find open PR for ${branchName}`);
      return null;
    });
  }

  function ensureBranch() {
    return github.branchExists(branchName)
    .then((branchExists) => {
      if (branchExists) {
        return updateExistingBranch();
      }
      return createNewBranch();
    });
  }

  function getNewPackageJson(content) {
    return packageJson.setNewValue(
      content,
      depType,
      depName,
      newVersion);
  }

  function updateExistingBranch() {
    // By default, we'll add a commit to the existing branch if necessary
    let parentBranch = branchName;
    logger.debug(`Checking if branch ${branchName} needs updating`);
    return getBranchPr()
    .then((pr) => {
      if (pr.mergeable_state === 'dirty') {
        logger.verbose(`Existing PR #${pr.number} is not mergeable`);
        if (pr.additions * pr.deletions === 1) {
          // No other changes except ours
          logger.verbose(`Rebasing ${branchName}`);
          // Setting parentBranch to undefined means that we'll use the default branch
          parentBranch = undefined;
        } else {
          logger.warning(`Cannot rebase ${branchName} as it has been modified`);
        }
      }
      return Promise.resolve();
    })
    .then(() => github.getFileContent(config.packageFile, parentBranch))
    .then((existingContent) => {
      const newContent = getNewPackageJson(existingContent);
      if (newContent === existingContent) {
        logger.debug(`Branch ${parentBranch} is already up-to-date`);
        return Promise.resolve();
      }
      logger.verbose(`Adding commit '${commitMessage}' to branch ${branchName}`);
      return github.commitFile(config.packageFile, newContent, commitMessage, parentBranch);
    })
    .then((commit) => {
      if (!commit) {
        return Promise.resolve();
      }
      return github.updateBranch(branchName, commit);
    });
  }

  function createNewBranch() {
    logger.verbose(`${depName}: creating new branch ${branchName}`);
    return github.getFileContent(config.packageFile)
    .then(getNewPackageJson)
    .then(newContent => github.commitFile(config.packageFile, newContent, commitMessage))
    .then(commit => github.createBranch(branchName, commit));
  }

  function getChangelog() {
    if (!upgrade.workingVersion || upgrade.workingVersion === upgrade.newVersion) {
      return Object.assign(upgrade, { changelog: '' });
    }
    const semverString = `>${upgrade.workingVersion} <=${upgrade.newVersion}`;
    logger.debug(`semverString: ${semverString}`);
    return changelog.generate(upgrade.depName, semverString)
      .then(changelog.markdown)
      .catch((error) => {
        logger.verbose(`getChangelog error: ${error}`);
      })
      .then((res) => {
        if (!res) {
          logger.verbose(`No changelog for ${depName}`);
          return '';
        }
        return `### Changelog\n${res}`;
      })
      .then(log => Object.assign(upgrade, { changelog: log }));
  }

  // Ensures that PR exists with matching title/body
  function ensurePr(upgradeWithChangeLog) {
    logger.debug('Ensuring PR');

    const prBody = handlebars.transform(config.prBody, upgradeWithChangeLog);

    return github.getBranchPr(branchName)
    .then(processExistingPr)
    .catch((error) => {
      logger.error(`${depName} failed to ensure PR: ${error}`);
    });

    // Create PR based on current state
    function createPr() {
      return github.createPr(branchName, prTitle, prBody).then((newPr) => {
        logger.info(`${depName}: Created PR #${newPr.number}`);
        return newPr.number;
      });
    }
    // Update PR based on current state
    function updatePr(existingPr) {
      logger.debug(`updatePr: ${existingPr.number}`);
      return github.updatePr(existingPr.number, prTitle, prBody).then(() => {
        logger.info(`${depName}: Updated PR #${existingPr.number}`);
        return Promise.resolve();
      });
    }
    // Process a returned PR
    function processExistingPr(existingPr) {
      if (!existingPr) {
        logger.debug(`Didn't find existing PR for branch ${branchName}`);
        // We need to create a new PR
        return createPr()
        .then(prNo => addLabels(prNo))
        .then(addAssignees);
      }
      // Check if existing PR needs updating
      logger.debug(`processExistingPr: ${existingPr.number}`);
      if (existingPr.title === prTitle && existingPr.body === prBody) {
        logger.verbose(`${depName}: PR #${existingPr.number} already up-to-date`);
        return Promise.resolve();
      }
      // PR must need updating
      return updatePr(existingPr);
    }

    // Add assignees to a PR
    function addAssignees(prNo) {
      if (config.assignees.length === 0) {
        logger.debug(`No assignees to add to ${prNo}`);
        return Promise.resolve();
      }
      return github.addAssignees(prNo, config.assignees);
    }

    // Add labels to a PR
    function addLabels(prNo) {
      if (config.labels.length === 0) {
        logger.debug(`No labels to add to ${prNo}`);
        return Promise.resolve(prNo);
      }
      return github.addLabels(prNo, config.labels)
      .then(() => prNo);
    }
  }
}
<|endoftext|>


<commit_before>/**
 * Cf. JavaScript Ninja, pp. 145ff.
 * http://ejohn.org/blog/simple-javascript-inheritance
 *
 * My version differs from the book in 3 ways:
 * 1) It doesn't check for whether the corresponding
 *    _super method is called by the overriding subclass method.
 * 2) It uses a different syntax that ALWAYS (even for constructor)
 *    requires the super method to be called explicitly. So, to
 *    call the super constructor, you write
 *    _super.init(params);
 *    NOT just _super(params);
 *    (the latter will result in an error)
 * 3) No functionality is added directly to the built-in
 *    JavaScript Object.
 */

(function () {
    var initializing = false;

    // generic base class
    this.Base = function() {};

    Base.extend = function (properties) {
        var _super = this.prototype,
            proto,
            name;

        initializing = true;
        proto = new this();
        initializing = false;

        // leaving out check for presence of "_super" string
        for (name in properties) {
            proto[name] = typeof properties[name] === "function" &&
                typeof _super[name] === "function" ? (function (name, fn) {
                    return function () {
                        this._super = this._super || {};
                        this._super[name] = _super[name];
                        return fn.apply(this, arguments);
                    };
                })(name, properties[name]) : properties[name];
        }

        function Class() {
            if (!initializing && this.init) {
                this.init.apply(this, arguments);
            }
        }

        Class.prototype = proto;
        Class.constructor = Class;
        Class.extend = arguments.callee;
        return Class;
    };
})();
<commit_msg>Remove arguments.callee from extend functionality<commit_after>/**
 * Cf. JavaScript Ninja, pp. 145ff.
 * http://ejohn.org/blog/simple-javascript-inheritance
 *
 * My version differs from the book in 3 ways:
 * 1) It doesn't check for whether the corresponding
 *    _super method is called by the overriding subclass method.
 * 2) It uses a different syntax that ALWAYS (even for constructor)
 *    requires the super method to be called explicitly. So, to
 *    call the super constructor, you write
 *    _super.init(params);
 *    NOT just _super(params);
 *    (the latter will result in an error)
 * 3) No functionality is added directly to the built-in
 *    JavaScript Object.
 */

(function () {
    var initializing = false;

    // generic base class
    this.Base = function() {};

    Base.extend = function extend(properties) {
        var _super = this.prototype,
            proto,
            name;

        initializing = true;
        proto = new this();
        initializing = false;

        // leaving out check for presence of "_super" string
        for (name in properties) {
            proto[name] = typeof properties[name] === "function" &&
                typeof _super[name] === "function" ? (function (name, fn) {
                    return function () {
                        this._super = this._super || {};
                        this._super[name] = _super[name];
                        return fn.apply(this, arguments);
                    };
                })(name, properties[name]) : properties[name];
        }

        function Class() {
            if (!initializing && this.init) {
                this.init.apply(this, arguments);
            }
        }

        Class.prototype = proto;
        Class.constructor = Class;
        Class.extend = extend;
        return Class;
    };
})();
<|endoftext|>


<commit_before>// The ConversationPanel module is designed to handle
// all display and behaviors of the conversation column of the app.
/* eslint no-unused-vars: "off" */
/* global Api: true, Common: true*/

var ConversationPanel = (function() {
  var settings = {
    selectors: {
      chatBox: '#scrollingChat',
      fromUser: '.from-user',
      fromWatson: '.from-watson',
      latest: '.latest'
    },
    authorTypes: {
      user: 'user',
      watson: 'watson'
    }
  };

  // Publicly accessible methods defined
  return {
    init: init,
    inputKeyDown: inputKeyDown
  };

  // Initialize the module
  function init() {
    chatUpdateSetup();
    // The client displays the initial message to the end user
    displayMessage(
      {output:
        {text: 'This Watson app shows how intents work. Try testing some below, using the examples in the app description.'}
      },
      settings.authorTypes.watson);
    setupInputBox();
  }

  // Set up callbacks on payload setters in Api module
  // This causes the displayMessage function to be called when messages are sent / received
  function chatUpdateSetup() {
    var currentRequestPayloadSetter = Api.setRequestPayload;
    Api.setRequestPayload = function(newPayloadStr) {
      currentRequestPayloadSetter.call(Api, newPayloadStr);
      displayMessage(JSON.parse(newPayloadStr), settings.authorTypes.user);
    };

    var currentResponsePayloadSetter = Api.setResponsePayload;
    Api.setResponsePayload = function(newPayloadStr) {
      currentResponsePayloadSetter.call(Api, newPayloadStr);
      displayMessage(JSON.parse(newPayloadStr), settings.authorTypes.watson);
    };
  }

  function setupInputBox() {
    var input = document.getElementById('textInput');
    var dummy = document.getElementById('textInputDummy');
    var padding = 3;

    if (dummy === null) {
      var dummyJson = {
        'tagName': 'div',
        'attributes': [{
          'name': 'id',
          'value': 'textInputDummy'
        }]
      };

      dummy = Common.buildDomElement(dummyJson);
      ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing'].forEach(function(index) {
        dummy.style[index] = window.getComputedStyle( input, null ).getPropertyValue( index );
      });

      document.body.appendChild(dummy);
    }

    input.addEventListener('input', function() {
      if (this.value === '') {
        this.classList.remove('underline');
        this.setAttribute('style', 'width:' + '100%');
        this.style.width = '100%';
      } else {
        this.classList.add('underline');
        var txtNode = document.createTextNode(this.value);
        dummy.innerText = txtNode.textContent;
        var widthValue = ( dummy.offsetWidth + padding) + 'px';
        this.setAttribute('style', 'width:' + widthValue);
        this.style.width = widthValue;
      }
    });

    fireEvent(input, 'input');
  }

  function fireEvent(element, event) {
    var evt;
    if (document.createEventObject) {
      // dispatch for IE
      evt = document.createEventObject();
      return element.fireEvent('on' + event, evt);
    }
    // otherwise, dispatch for Firefox, Chrome + others
    evt = document.createEvent('HTMLEvents');
    evt.initEvent(event, true, true); // event type,bubbling,cancelable
    return !element.dispatchEvent(evt);
  }

  // Display a user or Watson message that has just been sent/received
  function displayMessage(newPayload, typeValue) {
    var isUser = isUserMessage(typeValue);
    var textExists = (newPayload.input && newPayload.input.text)
      || (newPayload.output && newPayload.output.text);
    if (isUser !== null && textExists) {
      // Create new message DOM element
      var messageDiv = buildMessageDomElement(newPayload, isUser);
      var chatBoxElement = document.querySelector(settings.selectors.chatBox);
      var previousLatest = chatBoxElement.querySelector((isUser
              ? settings.selectors.fromUser : settings.selectors.fromWatson)
              + settings.selectors.latest);
      // Previous "latest" message is no longer the most recent
      if (previousLatest) {
        previousLatest.classList.remove('latest');
      }

      chatBoxElement.appendChild(messageDiv);
      // Class to start fade in animation
      messageDiv.classList.add('load');
      // Move chat to the most recent messages when new messages are added
      scrollToChatBottom();
    }
  }

  // Checks if the given typeValue matches with the user "name", the Watson "name", or neither
  // Returns true if user, false if Watson, and null if neither
  // Used to keep track of whether a message was from the user or Watson
  function isUserMessage(typeValue) {
    if (typeValue === settings.authorTypes.user) {
      return true;
    } else if (typeValue === settings.authorTypes.watson) {
      return false;
    }
    return null;
  }

  // Constructs new DOM element from a message payload
  function buildMessageDomElement(newPayload, isUser) {
    var dataObj = isUser ? newPayload.input : newPayload.output;

    var messageJson = {
      //

      'tagName': 'div',
      'classNames': ['segments'],
      'children': [{
        //

        'tagName': 'div',
        'classNames': [(isUser ? 'from-user' : 'from-watson'), 'latest'],
        'children': [{
          //

          'tagName': 'div',
          'classNames': ['message-inner'],
          'children': [{
            //
{messageText}

            'tagName': 'p',
            'text': dataObj.text
          }]
        }]
      }]
    };

    return Common.buildDomElement(messageJson);
  }

  // Scroll to the bottom of the chat window (to the most recent messages)
  // Note: this method will bring the most recent user message into view,
  //   even if the most recent message is from Watson.
  //   This is done so that the "context" of the conversation is maintained in the view,
  //   even if the Watson message is long.
  function scrollToChatBottom() {
    var scrollingChat = document.querySelector('#scrollingChat');
    var messages = scrollingChat.children;
    var lastMessage = messages[messages.length - 1];

    // If the most recent message is from Watson,
    // scroll the latest message to the top of the section, rather than the bottom
    var top = false;
    var scrollEl = lastMessage.querySelector(settings.selectors.fromWatson
            + settings.selectors.latest);
    if (scrollEl) {
      top = true;
    }

    // Scroll to the latest message sent by the user
    scrollEl = scrollingChat.querySelector(settings.selectors.fromUser
            + settings.selectors.latest);
    if (scrollEl) {
      scrollEl.scrollIntoView({
        'behavior': 'smooth', // Only supported by Firefox, but including doesn't break other browsers
        'block': (top ? 'start' : 'end')
      });
    }
  }

  // Handles the submission of input
  function inputKeyDown(event, inputBox) {
    // Submit on enter key, dis-allowing blank messages
    if (event.keyCode === 13 && inputBox.value) {
      // Retrieve the context from the previous server response
      var context;
      var latestResponse = Api.getResponsePayload();
      if (latestResponse) {
        context = latestResponse.context;
      }

      // Send the user message
      Api.sendRequest(inputBox.value, context);

      // Clear input box for further messages
      inputBox.value = '';
      fireEvent(inputBox, 'input');
    }
  }
}());
<commit_msg>Fix input tag bug and allow for greater cross-browser support<commit_after>// The ConversationPanel module is designed to handle
// all display and behaviors of the conversation column of the app.
/* eslint no-unused-vars: "off" */
/* global Api: true, Common: true*/

var ConversationPanel = (function() {
  var settings = {
    selectors: {
      chatBox: '#scrollingChat',
      fromUser: '.from-user',
      fromWatson: '.from-watson',
      latest: '.latest'
    },
    authorTypes: {
      user: 'user',
      watson: 'watson'
    }
  };

  // Publicly accessible methods defined
  return {
    init: init,
    inputKeyDown: inputKeyDown
  };

  // Initialize the module
  function init() {
    chatUpdateSetup();
    // The client displays the initial message to the end user
    displayMessage(
      {output:
        {text: 'This Watson app shows how intents work. Try testing some below, using the examples in the app description.'}
      },
      settings.authorTypes.watson);
    setupInputBox();
  }

  // Set up callbacks on payload setters in Api module
  // This causes the displayMessage function to be called when messages are sent / received
  function chatUpdateSetup() {
    var currentRequestPayloadSetter = Api.setRequestPayload;
    Api.setRequestPayload = function(newPayloadStr) {
      currentRequestPayloadSetter.call(Api, newPayloadStr);
      displayMessage(JSON.parse(newPayloadStr), settings.authorTypes.user);
    };

    var currentResponsePayloadSetter = Api.setResponsePayload;
    Api.setResponsePayload = function(newPayloadStr) {
      currentResponsePayloadSetter.call(Api, newPayloadStr);
      displayMessage(JSON.parse(newPayloadStr), settings.authorTypes.watson);
    };
  }

  function setupInputBox() {
    var input = document.getElementById('textInput');
    var dummy = document.getElementById('textInputDummy');
    var padding = 3;

    if (dummy === null) {
      var dummyJson = {
        'tagName': 'div',
        'attributes': [{
          'name': 'id',
          'value': 'textInputDummy'
        }]
      };

      dummy = Common.buildDomElement(dummyJson);
      ['font-size', 'font-style', 'font-weight', 'font-family', 'line-height', 'text-transform', 'letter-spacing'].forEach(function(index) {
        dummy.style[index] = window.getComputedStyle( input, null ).getPropertyValue( index );
      });

      document.body.appendChild(dummy);
    }

    input.addEventListener('input', function() {
      if (this.value === '') {
        this.classList.remove('underline');
        this.setAttribute('style', 'width:' + '100%');
        this.style.width = '100%';
      } else {
        this.classList.add('underline');
        var txtNode = document.createTextNode(this.value);
        dummy.textContent = txtNode.textContent;
        var widthValue = ( dummy.offsetWidth + padding) + 'px';
        this.setAttribute('style', 'width:' + widthValue);
        this.style.width = widthValue;
      }
    });

    fireEvent(input, 'input');
  }

  function fireEvent(element, event) {
    var evt;
    if (document.createEventObject) {
      // dispatch for IE
      evt = document.createEventObject();
      return element.fireEvent('on' + event, evt);
    }
    // otherwise, dispatch for Firefox, Chrome + others
    evt = document.createEvent('HTMLEvents');
    evt.initEvent(event, true, true); // event type,bubbling,cancelable
    return !element.dispatchEvent(evt);
  }

  // Display a user or Watson message that has just been sent/received
  function displayMessage(newPayload, typeValue) {
    var isUser = isUserMessage(typeValue);
    var textExists = (newPayload.input && newPayload.input.text)
      || (newPayload.output && newPayload.output.text);
    if (isUser !== null && textExists) {
      // Create new message DOM element
      var messageDiv = buildMessageDomElement(newPayload, isUser);
      var chatBoxElement = document.querySelector(settings.selectors.chatBox);
      var previousLatest = chatBoxElement.querySelector((isUser
              ? settings.selectors.fromUser : settings.selectors.fromWatson)
              + settings.selectors.latest);
      // Previous "latest" message is no longer the most recent
      if (previousLatest) {
        previousLatest.classList.remove('latest');
      }

      chatBoxElement.appendChild(messageDiv);
      // Class to start fade in animation
      messageDiv.classList.add('load');
      // Move chat to the most recent messages when new messages are added
      scrollToChatBottom();
    }
  }

  // Checks if the given typeValue matches with the user "name", the Watson "name", or neither
  // Returns true if user, false if Watson, and null if neither
  // Used to keep track of whether a message was from the user or Watson
  function isUserMessage(typeValue) {
    if (typeValue === settings.authorTypes.user) {
      return true;
    } else if (typeValue === settings.authorTypes.watson) {
      return false;
    }
    return null;
  }

  // Constructs new DOM element from a message payload
  function buildMessageDomElement(newPayload, isUser) {
    var dataObj = isUser ? newPayload.input : newPayload.output;

    var messageJson = {
      //

      'tagName': 'div',
      'classNames': ['segments'],
      'children': [{
        //

        'tagName': 'div',
        'classNames': [(isUser ? 'from-user' : 'from-watson'), 'latest'],
        'children': [{
          //

          'tagName': 'div',
          'classNames': ['message-inner'],
          'children': [{
            //
{messageText}

            'tagName': 'p',
            'text': dataObj.text
          }]
        }]
      }]
    };

    return Common.buildDomElement(messageJson);
  }

  // Scroll to the bottom of the chat window (to the most recent messages)
  // Note: this method will bring the most recent user message into view,
  //   even if the most recent message is from Watson.
  //   This is done so that the "context" of the conversation is maintained in the view,
  //   even if the Watson message is long.
  function scrollToChatBottom() {
    var scrollingChat = document.querySelector('#scrollingChat');
    var messages = scrollingChat.children;
    var lastMessage = messages[messages.length - 1];

    // If the most recent message is from Watson,
    // scroll the latest message to the top of the section, rather than the bottom
    var top = false;
    var scrollEl = lastMessage.querySelector(settings.selectors.fromWatson
            + settings.selectors.latest);
    if (scrollEl) {
      top = true;
    }

    // Scroll to the latest message sent by the user
    scrollEl = scrollingChat.querySelector(settings.selectors.fromUser
            + settings.selectors.latest);
    if (scrollEl) {
      scrollEl.scrollIntoView({
        'behavior': 'smooth', // Only supported by Firefox, but including doesn't break other browsers
        'block': (top ? 'start' : 'end')
      });
    }
  }

  // Handles the submission of input
  function inputKeyDown(event, inputBox) {
    // Submit on enter key, dis-allowing blank messages
    if (event.keyCode === 13 && inputBox.value) {
      // Retrieve the context from the previous server response
      var context;
      var latestResponse = Api.getResponsePayload();
      if (latestResponse) {
        context = latestResponse.context;
      }

      // Send the user message
      Api.sendRequest(inputBox.value, context);

      // Clear input box for further messages
      inputBox.value = '';
      fireEvent(inputBox, 'input');
    }
  }
}());
<|endoftext|>


<commit_before>(function ($) {
    "use strict";

    try {
        Object.defineProperties($, {
            flatten: {
                value: function(linkedList) {
                    var
                        flattened = [],
                        allowedPattern = /^(Array|Arguments)$/,
                        className = Object.className(linkedList);

                    if (className == "Arguments") {
                        linkedList = $.from(linkedList);
                    } else if (!allowedPattern.test(className)) {
                        return linkedList;
                    }

                    linkedList.forEach(function (i) {
                        className = Object.className(i);
                        // recursivity
                        flattened = flattened.concat(allowedPattern
                                                     .test(className) ?
                                                     $.flatten(i) :
                                                     i);
                    });

                    // this way all will be treated as String
                    // linkedList = linkedList.join().split(",");

                    return flattened;
                }
            }
        });

        Object.defineProperties($.prototype, {

            equivalenceIndexOf: {
                // value: function(comparator, startWithIndex = 0) {
                value: function(comparator, startWithIndex) {
                    if(typeof startWithIndex != "number") {startWithIndex = 0;}

                    var
                        found,
                        item,
                        i = startWithIndex,
                        index = i;

                    if (typeof comparator != "number" && !comparator) {
                        return -1;
                    }

                    for(i = startWithIndex; i < this.length; i++) {
                        item = this[i];
                        if (typeof item == "function") {
                            if (item == comparator) {
                                found = true;
                                break;
                            }
                        } else {
                            if (item === undefined) {
                                console.warn("Item for compare is " +
                                                "undefined!");
                            }

                            // change to overridable equals method. Maybe using valueOf
                            if (Object.areEquivalents(item, comparator)) {
                                found = true;
                                break;
                            }
                        }

                        index += 1;
                    }

                    if (found) {
                        return index;
                    }

                    return -1;
                }
            },

            uniq: {
                value: function() {
                    return this.reduce(function (init, v) {
                        if (init.equivalenceIndexOf(v) == -1) {
                            init.push(v);
                        }
                        return init;
                    }, []);
                }
            },

            includes: {
                value: function(items) {
                    var
                        args = Array.from(arguments).uniq(),
                        mainReference = this,
                        count = 0;

                    args.forEach(function (arg) {
                        if (mainReference.equivalenceIndexOf(arg) > -1) {
                            count += 1;
                        }
                    });

                    return count == args.length;
                }
            },

            delete: {
                value: function(index) {
                    return this.splice(index, 1);
                }
            },

            deleteValue: {
                value: function(amount) {
                    var
                        counter,
                        index,
                        mainReference = this,
                        deleted = [],
                        partsToDelete = Array.from(arguments);

                    partsToDelete.shift();
                    partsToDelete.forEach(function (i) {
                        counter = amount;
                        while(counter-- > 0 && mainReference.count(i)) {
                            index = mainReference.equivalenceIndexOf(i);
                            if (index > -1) {
                                deleted.push(mainReference.splice(index, 1)[0]);
                            }
                        }
                    });

                    return deleted;
                }
            },

            without: {
                value: function () {
                    var
                        parts = $.flatten(arguments),
                        clone = [];

                    clone = this.clone();

                    parts.forEach(function (i) {
                        clone.deleteValue(clone.count(i), i);
                    });

                    return clone;
                }
            },

            count: {
                value: function (item) {
                    var
                        clone = this.asc(),
                        index = -1,
                        count = 0;

                    while(true) {
                        index = clone.equivalenceIndexOf(item, index + 1);
                        if(index > -1) {
                            count++;
                        } else {
                            break;
                        }
                    }

                    return count;
                }
            },

            countSlice: {
                value: function (slice) {
                    var
                        arr = this.asc(),
                        count = 0,
                        i = parseInt(arr.firstFromASlice(slice));

                    if (i > -1) {
                        while (i < this.length) {
                            if (arr[i].trim().search(slice.trim()) > -1) {
                                count += 1;
                                i += 1;
                            } else {
                                break;
                            }
                        }
                    }

                    return count;
                }
            },

            differences: {
                value: function (sets) {
                    var
                        clone = this.clone();

                    sets = Array.from(arguments);

                    if (sets.length <= 0) {
                        return this;
                    }

                    clone.uniq().forEach(function(item) {
                        sets.forEach(function(set) {
                            clone.deleteValue(set.count(item), item);
                        });
                    });

                    return clone;
                }
            },

            flatten: {
                value: function () {
                    return $.flatten(this);
                }
            },

            capitalize: {
                value: function () {
                    var
                        clone = [];

                    this.forEach(function (value) {
                        if (typeof value == "string") {
                            clone.push(value.capitalize());
                        }
                    });

                    return clone;
                }
            },

            spaceOut: {
                value: function () {
                    var
                        i = 0,
                        clone = this.clone();

                    while(i < clone.lastIndex()) {
                        clone[i++] += " ";
                    }

                    return clone;
                }
            },

            compact: {
                value: function () {
                    var
                        newArr = [];

                    this.forEach(function (v) {
                        if (typeof v == "string") {
                            if (v !== "") {
                                newArr.push(v);
                            }
                        } else {
                            if (v !== undefined && v !== null) {
                                newArr.push(v);
                            }
                        }
                    });

                    return newArr;
                }
            },

            clone: {
                value: function () {
                    var
                        clone = [];

                    Object.assign(clone, this);

                    return clone;
                }
            },

            asc: {
                value: function () {
                    var
                        clone = this.clone(),
                        compareFunction = function (a, b) {
                            return a > b;
                        };

                    clone.sort(compareFunction);

                    return clone;
                }
            },

            desc: {
                value: function () {
                    var
                        clone = this.clone(),
                        compareFunction = function (a, b) {
                            return a < b;
                        };

                    clone.sort(compareFunction);

                    return clone;
                }
            },

            min: {
                value: function () {
                    return Math.min.apply(null, this);
                }
            },

            max: {
                value: function () {
                    return Math.max.apply(null, this);
                }
            },

            first: {
                value: function () {
                    return this[0];
                }
            },

            firstIndexOf: {
                value: function(item) {
                    return this.equivalenceIndexOf(item);
                }
            },

            lastIndexOf: {
                value: function (item) {
                    return this.lastIndex() - this.clone().reverse()
                        .equivalenceIndexOf(item);
                }
            },

            lastIndex: {
                value: function () {
                    return this.length - 1;
                }
            },

            last: {
                value: function () {
                    return this[this.lastIndex()];
                }
            },

            firstFromASlice: {
                value: function (slice) {
                    var
                        i,
                        value,
                        index = 0;

                    for(i in this) {
                        if (this.hasOwnProperty(i)) {
                            value = this[i];
                            if (value.search(slice) > -1) {
                                return index;
                            }
                            index += 1;
                        }
                    }

                    return -1;
                }
            },

            lastFromASlice: {
                value: function (slice) {
                    var
                        i, key,
                        object = Object.keys(this).desc();

                    for(i in object) {
                        if (object.hasOwnProperty(i)) {
                            key = object[i];
                            if (this[key].search(slice) > -1) {
                                return key;
                            }
                        }
                    }

                    return undefined;
                }
            }

        });
    } catch(e) {}

})(Array);
<commit_msg>adequations<commit_after>(function ($) {
    "use strict";

    try {
        Object.defineProperties($, {
            flatten: {
                value: function(linkedList) {
                    var
                        flattened = [],
                        allowedPattern = /^(Array|Arguments)$/,
                        className = Object.className(linkedList);

                    if (className == "Arguments") {
                        linkedList = $.from(linkedList);
                    } else if (!allowedPattern.test(className)) {
                        return linkedList;
                    }

                    linkedList.forEach(function (i) {
                        className = Object.className(i);
                        // recursivity
                        flattened = flattened.concat(allowedPattern
                                                     .test(className) ?
                                                     $.flatten(i) :
                                                     i);
                    });

                    // this way all will be treated as String
                    // linkedList = linkedList.join().split(",");

                    return flattened;
                }
            }
        });

        Object.defineProperties($.prototype, {

            clone: {
                value: function () {
                    var
                        clone = [];

                    Object.assign(clone, this);

                    return clone;
                }
            },

            indexOfEquivalence: {
                // value: function(comparator, startWithIndex = 0) {
                value: function(comparator, startWithIndex) {
                    if(typeof startWithIndex != "number") {startWithIndex = 0;}

                    var
                        found,
                        item,
                        i = startWithIndex,
                        index = i;

                    if (typeof comparator != "number" && !comparator) {
                        return -1;
                    }

                    for(i = startWithIndex; i < this.length; i++) {
                        item = this[i];
                        if (typeof item == "function") {
                            if (item == comparator) {
                                found = true;
                                break;
                            }
                        } else {
                            if (item === undefined) {
                                console.warn("Item for compare is " +
                                                "undefined!");
                            }

                            // change to overridable equals method. Maybe using valueOf
                            if (Object.areEquivalents(item, comparator)) {
                                found = true;
                                break;
                            }
                        }

                        index += 1;
                    }

                    if (found) {
                        return index;
                    }

                    return -1;
                }
            },

            uniq: {
                value: function() {
                    return this.reduce(function (init, v) {
                        if (init.indexOfEquivalence(v) == -1) {
                            init.push(v);
                        }
                        return init;
                    }, []);
                }
            },

            includes: {
                value: function(items) {
                    var
                        args = Array.from(arguments).uniq(),
                        mainReference = this,
                        count = 0;

                    args.forEach(function (arg) {
                        if (mainReference.indexOfEquivalence(arg) > -1) {
                            count += 1;
                        }
                    });

                    return count == args.length;
                }
            },

            delete: {
                value: function(index) {
                    return this.splice(index, 1);
                }
            },

            deleteValue: {
                value: function(amount) {
                    var
                        counter,
                        index,
                        mainReference = this,
                        deleted = [],
                        partsToDelete = Array.from(arguments);

                    partsToDelete.shift();
                    partsToDelete.forEach(function (i) {
                        counter = amount;
                        while(counter-- > 0 && mainReference.count(i)) {
                            index = mainReference.indexOfEquivalence(i);
                            if (index > -1) {
                                deleted.push(mainReference.splice(index, 1)[0]);
                            }
                        }
                    });

                    return deleted;
                }
            },

            without: {
                value: function () {
                    var
                        parts = $.flatten(arguments),
                        clone = [];

                    clone = this.clone();

                    parts.forEach(function (i) {
                        clone.deleteValue(clone.count(i), i);
                    });

                    return clone;
                }
            },

            count: {
                value: function (item) {
                    var
                        clone = this.asc(),
                        index = -1,
                        count = 0;

                    while(true) {
                        index = clone.indexOfEquivalence(item, index + 1);
                        if(index > -1) {
                            count++;
                        } else {
                            break;
                        }
                    }

                    return count;
                }
            },

            countSlice: {
                value: function (slice) {
                    var
                        arr = this.asc(),
                        count = 0,
                        i = parseInt(arr.firstFromASlice(slice));

                    if (i > -1) {
                        while (i < this.length) {
                            if (arr[i].trim().search(slice.trim()) > -1) {
                                count += 1;
                                i += 1;
                            } else {
                                break;
                            }
                        }
                    }

                    return count;
                }
            },

            differences: {
                value: function (sets) {
                    var
                        clone = this.clone();

                    sets = Array.from(arguments);

                    if (sets.length <= 0) {
                        return this;
                    }

                    clone.uniq().forEach(function(item) {
                        sets.forEach(function(set) {
                            clone.deleteValue(set.count(item), item);
                        });
                    });

                    return clone;
                }
            },

            flatten: {
                value: function () {
                    return $.flatten(this);
                }
            },

            capitalize: {
                value: function () {
                    var
                        clone = [];

                    this.forEach(function (value) {
                        if (typeof value == "string") {
                            clone.push(value.capitalize());
                        }
                    });

                    return clone;
                }
            },

            spaceOut: {
                value: function () {
                    var
                        i = 0,
                        clone = this.clone();

                    while(i < clone.lastIndex()) {
                        clone[i++] += " ";
                    }

                    return clone;
                }
            },

            compact: {
                value: function () {
                    var
                        newArr = [];

                    this.forEach(function (v) {
                        if (typeof v == "string") {
                            if (v !== "") {
                                newArr.push(v);
                            }
                        } else {
                            if (v !== undefined && v !== null) {
                                newArr.push(v);
                            }
                        }
                    });

                    return newArr;
                }
            },

            asc: {
                value: function () {
                    var
                        clone = this.clone(),
                        compareFunction = function (a, b) {
                            return a > b;
                        };

                    clone.sort(compareFunction);

                    return clone;
                }
            },

            desc: {
                value: function () {
                    var
                        clone = this.clone(),
                        compareFunction = function (a, b) {
                            return a < b;
                        };

                    clone.sort(compareFunction);

                    return clone;
                }
            },

            min: {
                value: function () {
                    return Math.min.apply(null, this);
                }
            },

            max: {
                value: function () {
                    return Math.max.apply(null, this);
                }
            },

            first: {
                value: function () {
                    return this[0];
                }
            },

            firstIndexOf: {
                value: function(item) {
                    return this.indexOfEquivalence(item);
                }
            },

            lastIndex: {
                value: function () {
                    return this.length - 1;
                }
            },

            lastIndexOf: {
                value: function (item) {
                    return this.lastIndex() - this.clone().reverse()
                        .indexOfEquivalence(item);
                }
            },

            last: {
                value: function () {
                    return this[this.lastIndex()];
                }
            },

            firstFromASlice: {
                value: function (slice) {
                    var
                        i,
                        value,
                        index = 0;

                    for(i in this) {
                        if (this.hasOwnProperty(i)) {
                            value = this[i];
                            if (value.search(slice) > -1) {
                                return index;
                            }
                            index += 1;
                        }
                    }

                    return -1;
                }
            },

            lastFromASlice: {
                value: function (slice) {
                    var
                        i, key,
                        object = Object.keys(this).desc();

                    for(i in object) {
                        if (object.hasOwnProperty(i)) {
                            key = object[i];
                            if (this[key].search(slice) > -1) {
                                return key;
                            }
                        }
                    }

                    return undefined;
                }
            }

        });
    } catch(e) {}

})(Array);
<|endoftext|>


<commit_before>const config = require('config')
const request = require('request')
const logger = require('../../../lib/logger.js')()
const redis = require('redis')

const IP_GEOLOCATION_TIMEOUT = 500

exports.get = function (req, res) {
  if (req.headers.host !== config.app_host_port) {
    res.status(403).json({ status: 403, error: 'Not allowed to access API' })
    return
  }

  const requestGeolocation = function (isRedisConnected = true) {
    console.log('requesting geolocation')
    const url = `${config.geoip.protocol}${config.geoip.host}?access_key=${config.geoip.api_key}`

    request(url, { timeout: IP_GEOLOCATION_TIMEOUT }, function (error, response, body) {
      if (error) {
        logger.error(error)
        // Error codes 101 and 104 are provided by ipstack to specify invalid/missing api key
        // and maximum monthly API requests reached, respectively. Since ipstack already has
        // error messages included with these errors, just send the message to the front-end.
        if (error.code === 101 || error.code === 104) {
          res.status(error.code).json({ status: error.code, error: error.info })
        } else if (error.code === 'ETIMEDOUT') {
          // If request takes longer than declared IP_GEOLOCATION_TIMEOUT, return status code 408
          res.status(408).json({ status: 408, error: 'Request timed out' })
        }
        return
      }

      if (isRedisConnected && req.ip) {
        client.set(req.ip, body, redis.print)
      }

      res.status(200).send(body)
    })
  }

  const client = redis.createClient(config.redis.port, config.redis.hostname)

  // If Redis cache not connecting, stop trying to connect, log the error,
  // and request geolocation from ipstack.
  client.on('error', function (error) {
    logger.error(error)
    client.end(true)
    requestGeolocation(false)
  })

  client.on('connect', function () {
    console.log('Connected to Redis')
    client.auth(config.redis.secret, function (err) {
      if (err) {
        // If unable to connect to Redis cache, log error
        // and request geolocation from ipstack automatically.
        logger.error(err)
        requestGeolocation(false)
      } else {
        client.get(req.ip, function (error, reply) {
          if (error) {
            // If an error occurs while trying to get key,
            // request geolocation from ipstack automatically.
            logger.error(error)
            requestGeolocation()
            return
          }

          if (!reply || config.redis.hostname === 'localhost') {
            // If no matching key or Streetmix is being run locally,
            // request geolocation from ipstack.
            requestGeolocation()
          } else {
            res.status(200).send(reply)
          }
        })
      }
    })
  })
}
<commit_msg>add check to see if redis needs to authenticate password<commit_after>const config = require('config')
const request = require('request')
const logger = require('../../../lib/logger.js')()
const redis = require('redis')

const IP_GEOLOCATION_TIMEOUT = 500

exports.get = function (req, res) {
  if (req.headers.host !== config.app_host_port) {
    res.status(403).json({ status: 403, error: 'Not allowed to access API' })
    return
  }

  const requestGeolocation = function (isRedisConnected = true) {
    console.log('requesting geolocation')
    const url = `${config.geoip.protocol}${config.geoip.host}?access_key=${config.geoip.api_key}`

    request(url, { timeout: IP_GEOLOCATION_TIMEOUT }, function (error, response, body) {
      if (error) {
        logger.error(error)
        // Error codes 101 and 104 are provided by ipstack to specify invalid/missing api key
        // and maximum monthly API requests reached, respectively. Since ipstack already has
        // error messages included with these errors, just send the message to the front-end.
        if (error.code === 101 || error.code === 104) {
          res.status(error.code).json({ status: error.code, error: error.info })
        } else if (error.code === 'ETIMEDOUT') {
          // If request takes longer than declared IP_GEOLOCATION_TIMEOUT, return status code 408
          res.status(408).json({ status: 408, error: 'Request timed out' })
        }
        return
      }

      if (isRedisConnected && req.ip) {
        client.set(req.ip, body, redis.print)
      }

      res.status(200).send(body)
    })
  }

  const client = redis.createClient(config.redis.port, config.redis.hostname)

  // If Redis cache not connecting, stop trying to connect, log the error,
  // and request geolocation from ipstack.
  client.on('error', function (error) {
    logger.error(error)
    client.end(true)
    requestGeolocation(false)
  })

  client.on('connect', function () {
    console.log('Connected to Redis')

    let authenticated = true
    if (config.redis.secret) {
      client.auth(config.redis.secret, function (err) {
        if (err) {
          // If unable to connect to Redis cache, log error
          // and request geolocation from ipstack automatically.
          logger.error(err)
          authenticated = false
          requestGeolocation(false)
        }
      })
    }

    if (authenticated) {
      client.get(req.ip, function (error, reply) {
        if (error) {
          // If an error occurs while trying to get key,
          // request geolocation from ipstack automatically.
          logger.error(error)
          requestGeolocation()
          return
        }

        if (!reply || config.redis.hostname === 'localhost') {
          // If no matching key or Streetmix is being run locally,
          // request geolocation from ipstack.
          requestGeolocation()
        } else {
          res.status(200).send(reply)
        }
      })
    }
  })
}
<|endoftext|>


<commit_before>var _ = require('lodash');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

/**
 * Worker
 * Pop a queue to execute some task
 */

/**
 * Create worker
 * @param Queue queue Queue
 * @param object driver Driver
 * @param string name Worker name
 * @param function action Action
 * @param object options Options
 */
function Worker (queue, driver, name, action, options) {
  EventEmitter.call(this);

  this.options = _.extend({
    type: 'FIFO',
    loopSleepTime: 0,
    pauseSleepTime: 5,
    waitingTimeout: 1,
    dataPerTick: 1,
    expire: 17280000
  }, options);

  this.queue = queue;
  this.driver = driver;
  this.action = action;
  this.completeName = this.queue.getName() + ':' + name;
  this.initialized = false;
};

util.inherits(Worker, EventEmitter);

/**
 * Status "STARTED"
 * @type string
 */
Worker.STATUS_STARTED = 'STARTED';

/**
 * Status "PAUSED"
 * @type string
 */
Worker.STATUS_PAUSED = 'PAUSE';

/**
 * Status "WORKING"
 * @type string
 */
Worker.STATUS_WORKING = 'WORKING';

/**
 * Status "WAITING"
 * @type string
 */
Worker.STATUS_WAITING = 'WAITING';

/**
 * Status "SLEEPING"
 * @type string
 */
Worker.STATUS_SLEEPING = 'SLEEPING';

/**
 * Type "FIFO"
 * @type string
 */
Worker.TYPE_FIFO = 'FIFO';

/**
 * Type "LIFO"
 * @type string
 */
Worker.TYPE_LIFO = 'LIFO';

/**
 * Initialize the worker
 */
Worker.prototype.initialize = function (callback) {
  if (this.initialized)
    return callback();

  var count = 8,
  inc = _.bind(function(err, res) {
    if (err)
      return callback(err, res);

    count --;

    if (count === 0) {
      this.initialized = true;
      return callback(err, res);
    }
  }, this);

  this.driver.initialize(_.bind(function () {
    this.setWaitingTimeout(this.options.waitingTimeout, inc);
    this.setLoopSleepTime(this.options.loopSleepTime, inc);
    this.setPauseSleepTime(this.options.pauseSleepTime, inc);
    this.setDataPerTick(this.options.dataPerTick, inc);
    this.setAction(this.action, inc);
    this.setType(this.options.type, inc);
    this.setLanguage(inc);
    this.setProcessId(inc);
  }, this));
};

/**
 * Start the worker
 * @param function callback Callback
 */
Worker.prototype.start = function (callback) {
  this.initialize(_.bind(function () {
    this.setStatus(Worker.STATUS_STARTED, callback);

    var endDate = new Date(new Date().getTime() + (this.options.expire * 1000)).toJSON();
    this.setEndDate(endDate);

    this.process();
  }, this));
};

/**
 * Pause the worker
 * @param function callback Callback
 */
Worker.prototype.pause = function (callback) {
  this.initialize(_.bind(function () {
    this.setStatus(Worker.STATUS_PAUSED, callback);
  }));
};

/**
 * Stop the worker
 * @param function callback Callback
 */
Worker.prototype.stop = function (callback) {
  this.initialize(_.bind(function() {
    this.setEndDate(null, callback);
  }));
};

/**
 * Process of the worker
 */
Worker.prototype.process = function () {
  this.getInfos(_.bind(function (err, infos) {
    if (! infos.end_date || new Date().getTime() > new Date(infos.end_date).getTime()) {
      this.queue.driver.close();
      this.driver.close();
      return ;
    }

    if(infos.status === Worker.STATUS_PAUSED) {
      var pauseTime = infos.pause_sleep_time * 1000;
      Worker.prototype.setEndDate(new Date(new Date().getTime() + pauseTime).toJSON());
      setTimeout(_.bind(this.process, this), pauseTime);
      return ;
    }

    var typeFunction = infos.type === Worker.TYPE_FIFO ? 'lpop' : 'rpop';

    this.queue[typeFunction](infos.data_per_tick, _.bind(function (err, result) {

      if (! result[0]) {
        setTimeout(_.bind(this.process, this), infos.waiting_timeout * 1000);
        this.setStatus(Worker.STATUS_WAITING);
        return ;
      }

      var callback = _.bind(function () {
        this.emit('job complete');
        if(infos.loop_sleep === 0)
          return this.process.call(this);

        setTimeout(_.bind(this.process, this), infos.loop_sleep * 1000);
        this.setStatus(Worker.STATUS_SLEEPING);
      }, this);

      this.setStatus(Worker.STATUS_WORKING);
      this.action.call(null, result, callback, this);
    }, this));
  }, this));
};

/**
 * Set the status of the worker
 * @param string status Worker status
 * @param function callback Callback
 */
Worker.prototype.setStatus = function (status, callback) {
  var worker = this;

  this.driver.hset(this.completeName, 'status', status, function() {
    worker.emit('status change', status);
    if (callback) {
      callback.call(null, arguments);
    }
  });

  var date = (new Date()).toJSON();

  this.driver.hset(this.completeName, 'status_changedate', date);

  if (status === Worker.STATUS_WORKING)
    this.driver.hincrby(this.completeName, 'cpt_action', 1);

  if (status === Worker.STATUS_STARTED) {
    this.driver.hset(this.completeName, 'cpt_action', 0);
    this.driver.hset(this.completeName, 'start_date', date);
  }
};

/**
 * Set the end date
 * @param string date Date in format JSON
 * @param function callback Callback
 */
Worker.prototype.setEndDate = function (date, callback) {
  this.driver.hset(this.completeName, 'end_date', date, callback);
};

/**
 * Set the loop sleep time of the worker
 * @param number time Time in seconds
 * @param function callback Callback
 */
Worker.prototype.setLoopSleepTime = function (time, callback) {
  this.driver.hset(this.completeName, 'loop_sleep', time, callback);
};

/**
 * Set the pause sleep time of the worker
 * @param number time Time in seconds
 * @param function callback Callback
 */
Worker.prototype.setPauseSleepTime = function (time, callback) {
  this.driver.hset(this.completeName, 'pause_sleep_time', time, callback);
};

/**
 * Set the number of data to get per tick
 * @param number number Data per tick
 * @param function callback Callback
 */
Worker.prototype.setDataPerTick = function (number, callback) {
  this.driver.hset(this.completeName, 'data_per_tick', number, callback);
};

/**
 * Set the waiting timeout of the worker
 * @param number timeout Timeout in seconds
 * @param function callback Callback
 */
Worker.prototype.setWaitingTimeout = function (timeout, callback) {
  this.driver.hset(this.completeName, 'waiting_timeout', timeout, callback);
};

/**
 * Set the action of the worker
 * @param function action Action
 * @param function callback Callback
 */
Worker.prototype.setAction = function (action, callback) {
  this.action = action;
  this.driver.hset(this.completeName, 'action', action.toString(), callback);
};

/**
 * Set the type of the worker (FIFO, LIFO)
 * @param string type Type
 * @param function callback Callback
 */
Worker.prototype.setType = function (type, callback) {
  this.driver.hset(this.completeName, 'type', type, callback);
};

/**
 * Set the language to "nodejs"
 * @param function callback Callback
 */
Worker.prototype.setLanguage = function (callback) {
  this.driver.hset(this.completeName, 'language', 'nodejs', callback);
};

/**
 * Set the pid
 * @param function callback Callback
 */
Worker.prototype.setProcessId = function (callback) {
  this.driver.hset(this.completeName, 'process_id', process.pid, callback);
};

/**
 * Return the complete name of the worker
 * @return string The complete name of the worker
 */
Worker.prototype.getCompleteName = function() {
  return this.driver.getCompleteWorkerName(this.completeName);
};

/**
 * Get worker infos
 * @param function callback Callback
 */
Worker.prototype.getInfos = function (callback) {
  this.driver.hgetall(this.completeName, callback);
};

exports.Worker = Worker;
<commit_msg>handle errors before emitting new state<commit_after>var _ = require('lodash');
var util = require('util');
var EventEmitter = require('events').EventEmitter;

/**
 * Worker
 * Pop a queue to execute some task
 */

/**
 * Create worker
 * @param Queue queue Queue
 * @param object driver Driver
 * @param string name Worker name
 * @param function action Action
 * @param object options Options
 */
function Worker (queue, driver, name, action, options) {
  EventEmitter.call(this);

  this.options = _.extend({
    type: 'FIFO',
    loopSleepTime: 0,
    pauseSleepTime: 5,
    waitingTimeout: 1,
    dataPerTick: 1,
    expire: 17280000
  }, options);

  this.queue = queue;
  this.driver = driver;
  this.action = action;
  this.completeName = this.queue.getName() + ':' + name;
  this.initialized = false;
};

util.inherits(Worker, EventEmitter);

/**
 * Status "STARTED"
 * @type string
 */
Worker.STATUS_STARTED = 'STARTED';

/**
 * Status "PAUSED"
 * @type string
 */
Worker.STATUS_PAUSED = 'PAUSE';

/**
 * Status "WORKING"
 * @type string
 */
Worker.STATUS_WORKING = 'WORKING';

/**
 * Status "WAITING"
 * @type string
 */
Worker.STATUS_WAITING = 'WAITING';

/**
 * Status "SLEEPING"
 * @type string
 */
Worker.STATUS_SLEEPING = 'SLEEPING';

/**
 * Type "FIFO"
 * @type string
 */
Worker.TYPE_FIFO = 'FIFO';

/**
 * Type "LIFO"
 * @type string
 */
Worker.TYPE_LIFO = 'LIFO';

/**
 * Initialize the worker
 */
Worker.prototype.initialize = function (callback) {
  if (this.initialized)
    return callback();

  var count = 8,
  inc = _.bind(function(err, res) {
    if (err)
      return callback(err, res);

    count --;

    if (count === 0) {
      this.initialized = true;
      return callback(err, res);
    }
  }, this);

  this.driver.initialize(_.bind(function () {
    this.setWaitingTimeout(this.options.waitingTimeout, inc);
    this.setLoopSleepTime(this.options.loopSleepTime, inc);
    this.setPauseSleepTime(this.options.pauseSleepTime, inc);
    this.setDataPerTick(this.options.dataPerTick, inc);
    this.setAction(this.action, inc);
    this.setType(this.options.type, inc);
    this.setLanguage(inc);
    this.setProcessId(inc);
  }, this));
};

/**
 * Start the worker
 * @param function callback Callback
 */
Worker.prototype.start = function (callback) {
  this.initialize(_.bind(function () {
    this.setStatus(Worker.STATUS_STARTED, callback);

    var endDate = new Date(new Date().getTime() + (this.options.expire * 1000)).toJSON();
    this.setEndDate(endDate);

    this.process();
  }, this));
};

/**
 * Pause the worker
 * @param function callback Callback
 */
Worker.prototype.pause = function (callback) {
  this.initialize(_.bind(function () {
    this.setStatus(Worker.STATUS_PAUSED, callback);
  }));
};

/**
 * Stop the worker
 * @param function callback Callback
 */
Worker.prototype.stop = function (callback) {
  this.initialize(_.bind(function() {
    this.setEndDate(null, callback);
  }));
};

/**
 * Process of the worker
 */
Worker.prototype.process = function () {
  this.getInfos(_.bind(function (err, infos) {
    if (! infos.end_date || new Date().getTime() > new Date(infos.end_date).getTime()) {
      this.queue.driver.close();
      this.driver.close();
      return ;
    }

    if(infos.status === Worker.STATUS_PAUSED) {
      var pauseTime = infos.pause_sleep_time * 1000;
      Worker.prototype.setEndDate(new Date(new Date().getTime() + pauseTime).toJSON());
      setTimeout(_.bind(this.process, this), pauseTime);
      return ;
    }

    var typeFunction = infos.type === Worker.TYPE_FIFO ? 'lpop' : 'rpop';

    this.queue[typeFunction](infos.data_per_tick, _.bind(function (err, result) {

      if (! result[0]) {
        setTimeout(_.bind(this.process, this), infos.waiting_timeout * 1000);
        this.setStatus(Worker.STATUS_WAITING);
        return ;
      }

      var callback = _.bind(function () {
        this.emit('job complete');
        if(infos.loop_sleep === 0)
          return this.process.call(this);

        setTimeout(_.bind(this.process, this), infos.loop_sleep * 1000);
        this.setStatus(Worker.STATUS_SLEEPING);
      }, this);

      this.setStatus(Worker.STATUS_WORKING);
      this.action.call(null, result, callback, this);
    }, this));
  }, this));
};

/**
 * Set the status of the worker
 * @param string status Worker status
 * @param function callback Callback
 */
Worker.prototype.setStatus = function (status, callback) {
  var worker = this;

  this.driver.hset(this.completeName, 'status', status, function(err) {
    if (err === null) {
      worker.emit('status change', status);
    }

    if (callback) {
      callback.apply(null, arguments);
    }
  });

  var date = (new Date()).toJSON();

  this.driver.hset(this.completeName, 'status_changedate', date);

  if (status === Worker.STATUS_WORKING)
    this.driver.hincrby(this.completeName, 'cpt_action', 1);

  if (status === Worker.STATUS_STARTED) {
    this.driver.hset(this.completeName, 'cpt_action', 0);
    this.driver.hset(this.completeName, 'start_date', date);
  }
};

/**
 * Set the end date
 * @param string date Date in format JSON
 * @param function callback Callback
 */
Worker.prototype.setEndDate = function (date, callback) {
  this.driver.hset(this.completeName, 'end_date', date, callback);
};

/**
 * Set the loop sleep time of the worker
 * @param number time Time in seconds
 * @param function callback Callback
 */
Worker.prototype.setLoopSleepTime = function (time, callback) {
  this.driver.hset(this.completeName, 'loop_sleep', time, callback);
};

/**
 * Set the pause sleep time of the worker
 * @param number time Time in seconds
 * @param function callback Callback
 */
Worker.prototype.setPauseSleepTime = function (time, callback) {
  this.driver.hset(this.completeName, 'pause_sleep_time', time, callback);
};

/**
 * Set the number of data to get per tick
 * @param number number Data per tick
 * @param function callback Callback
 */
Worker.prototype.setDataPerTick = function (number, callback) {
  this.driver.hset(this.completeName, 'data_per_tick', number, callback);
};

/**
 * Set the waiting timeout of the worker
 * @param number timeout Timeout in seconds
 * @param function callback Callback
 */
Worker.prototype.setWaitingTimeout = function (timeout, callback) {
  this.driver.hset(this.completeName, 'waiting_timeout', timeout, callback);
};

/**
 * Set the action of the worker
 * @param function action Action
 * @param function callback Callback
 */
Worker.prototype.setAction = function (action, callback) {
  this.action = action;
  this.driver.hset(this.completeName, 'action', action.toString(), callback);
};

/**
 * Set the type of the worker (FIFO, LIFO)
 * @param string type Type
 * @param function callback Callback
 */
Worker.prototype.setType = function (type, callback) {
  this.driver.hset(this.completeName, 'type', type, callback);
};

/**
 * Set the language to "nodejs"
 * @param function callback Callback
 */
Worker.prototype.setLanguage = function (callback) {
  this.driver.hset(this.completeName, 'language', 'nodejs', callback);
};

/**
 * Set the pid
 * @param function callback Callback
 */
Worker.prototype.setProcessId = function (callback) {
  this.driver.hset(this.completeName, 'process_id', process.pid, callback);
};

/**
 * Return the complete name of the worker
 * @return string The complete name of the worker
 */
Worker.prototype.getCompleteName = function() {
  return this.driver.getCompleteWorkerName(this.completeName);
};

/**
 * Get worker infos
 * @param function callback Callback
 */
Worker.prototype.getInfos = function (callback) {
  this.driver.hgetall(this.completeName, callback);
};

exports.Worker = Worker;
<|endoftext|>


<commit_before>import Round from '../app/model/round.js';

test('constructor', () => {
    // Arrange
    let object_under_test = new Round();

    // Act
    // Empty

    // Assert
    expect(object_under_test.toString()).toBe("0.0");
});

test('basic dart increment', () => {
    // Arrange
    let object_under_test = new Round();

    // Act
    object_under_test.nextDart();

    // Assert
    expect(object_under_test.toString()).toBe("0.1");
});

test('dart increment 2x', () => {
    // Arrange
    let object_under_test = new Round();
    object_under_test.nextDart();

    // Act
    object_under_test.nextDart();

    // Assert
    expect(object_under_test.toString()).toBe("0.2");
});

test('dart increment rollover', () => {
    // Arrange
    let object_under_test = new Round();
    object_under_test.nextDart();
    object_under_test.nextDart();

    // Act
    object_under_test.nextDart();

    // Assert
    expect(object_under_test.toString()).toBe("1.0");
});

test('Basic round increment', () => {
    // Arrange
    let object_under_test = new Round();

    // Act
    object_under_test.nextRound();

    // Assert
    expect(object_under_test.toString()).toBe("1.0");
});

test('Round increment x2', () => {
    // Arrange
    let object_under_test = new Round();
    object_under_test.nextRound();

    // Act
    object_under_test.nextRound();

    // Assert
    expect(object_under_test.toString()).toBe("2.0");
});

test('Round increment x2 then dart', () => {
    // Arrange
    let object_under_test = new Round();
    object_under_test.nextRound();
    object_under_test.nextRound();

    // Act
    object_under_test.nextDart();

    // Assert
    expect(object_under_test.toString()).toBe("2.1");
});

test('Round increment x2 then dart', () => {
    // Arrange
    let object_under_test = new Round();
    object_under_test.nextRound();
    object_under_test.nextRound();
    object_under_test.nextDart();

    // Act
    object_under_test.nextRound();

    // Assert
    expect(object_under_test.toString()).toBe("3.0");
});
<commit_msg>[DARTS-3] Added untested scoreboard<commit_after>import Round from '../app/model/round.js';

test('constructor', () => {
    // Arrange
    let object_under_test = new Round();

    // Act
    // Empty

    // Assert
    expect(object_under_test.toString()).toBe("0.0");
    expect(object_under_test.getTotalThrows()).toBe(0);
    expect(object_under_test.getCompletedRounds()).toBe(0);
});

test('basic dart increment', () => {
    // Arrange
    let object_under_test = new Round();

    // Act
    object_under_test.nextDart();

    // Assert
    expect(object_under_test.toString()).toBe("0.1");
    expect(object_under_test.getTotalThrows()).toBe(1);
    expect(object_under_test.getCompletedRounds()).toBe(0);
});

test('dart increment 2x', () => {
    // Arrange
    let object_under_test = new Round();
    object_under_test.nextDart();

    // Act
    object_under_test.nextDart();

    // Assert
    expect(object_under_test.toString()).toBe("0.2");
    expect(object_under_test.getTotalThrows()).toBe(2);
    expect(object_under_test.getCompletedRounds()).toBe(0);
});

test('dart increment rollover', () => {
    // Arrange
    let object_under_test = new Round();
    object_under_test.nextDart();
    object_under_test.nextDart();

    // Act
    object_under_test.nextDart();

    // Assert
    expect(object_under_test.toString()).toBe("1.0");
    expect(object_under_test.getTotalThrows()).toBe(3);
    expect(object_under_test.getCompletedRounds()).toBe(1);
});

test('Basic round increment', () => {
    // Arrange
    let object_under_test = new Round();

    // Act
    object_under_test.nextRound();

    // Assert
    expect(object_under_test.toString()).toBe("1.0");
    expect(object_under_test.getTotalThrows()).toBe(3);
    expect(object_under_test.getCompletedRounds()).toBe(1);
});

test('Round increment x2', () => {
    // Arrange
    let object_under_test = new Round();
    object_under_test.nextRound();

    // Act
    object_under_test.nextRound();

    // Assert
    expect(object_under_test.toString()).toBe("2.0");
    expect(object_under_test.getTotalThrows()).toBe(6);
    expect(object_under_test.getCompletedRounds()).toBe(2);
});

test('Round increment x2 then dart', () => {
    // Arrange
    let object_under_test = new Round();
    object_under_test.nextRound();
    object_under_test.nextRound();

    // Act
    object_under_test.nextDart();

    // Assert
    expect(object_under_test.toString()).toBe("2.1");
    expect(object_under_test.getTotalThrows()).toBe(7);
    expect(object_under_test.getCompletedRounds()).toBe(2);
});

test('Round increment x2 then dart', () => {
    // Arrange
    let object_under_test = new Round();
    object_under_test.nextRound();
    object_under_test.nextRound();
    object_under_test.nextDart();

    // Act
    object_under_test.nextRound();

    // Assert
    expect(object_under_test.toString()).toBe("3.0");
    expect(object_under_test.getTotalThrows()).toBe(9);
    expect(object_under_test.getCompletedRounds()).toBe(3);
});
<|endoftext|>