<commit_before>var stacktrace = require("stack-trace"),
    fs = require('fs'),
    Promise = require('promise'),
    Utils = require("./utils"),
    Configuration = require("./configuration");

function loadCode (error, callback) {
    var padding = 4; // Padding of either side of the frame's line (4 actually means 3...)

    Promise.all(error.stacktrace.map(function (frame) {
        if (frame.inProject) {
            return new Promise(function (resolve) {
                fs.readFile(frame.path, function (err, data) {
                    if ( ! err) {
                        var fileLines = data.toString().split('\n');

                        frame.code = {};

                        for (var i = frame.lineNumber - padding; i < (frame.lineNumber - 1) + padding; i++) {
                            if (typeof fileLines[i] === 'string') {
                                frame.code[i + 1] = fileLines[i];
                            }
                        }
                    }

                    resolve();
                });
            });
        }
    })).then(callback);
}

function processCallSites (callSites) {
    return callSites.map(function(callSite) {
        var frame, _ref;
        frame = {
           file: callSite.getFileName(),
           path: callSite.getFileName(),
           method: callSite.getMethodName() || callSite.getFunctionName() || "none",
           lineNumber: callSite.getLineNumber(),
           columnNumber: callSite.getColumnNumber()
        };

        if (Configuration.projectRoot && frame.file && frame.file.indexOf(Configuration.projectRoot) === 0) {
            frame.inProject = callSite.getFileName().indexOf("node_modules") === -1;
            if (!frame.inProject) {
                delete frame.inProject;
            }
            frame.file = frame.file.substr(Configuration.projectRoot.length + 1);
        }
        return frame;
    });
}

function Error(error, errorClass) {
    var callSites;
    if (Utils.typeOf(error) === "string") {
        this.message = error;
        this.errorClass = errorClass || "Error";
    } else if (error) {
        this.message = error.message;
        this.errorClass = errorClass || error.constructor.name || error.name || "Error";
    } else {
        this.message = "[unknown]";
        this.errorClass = errorClass || "Error";
    }
    callSites = stacktrace.parse(error);
    if (callSites.length === 0) {
        callSites = stacktrace.get();
    }
    this.stacktrace = processCallSites(callSites, Configuration.projectRoot);
}

Error.prototype.loadCode = function (callback) {
    loadCode(this, callback);
};

Error.buildErrors = function(error, errorClass) {
    var returnArray;
    returnArray = [new module.exports(error, errorClass)];
    if (error.oauthError) {
        returnArray.push(new module.exports(error.oauthError));
    }
    return returnArray;
};

module.exports = Error;
<commit_msg>Prefer error.name over constructor.name<commit_after>var stacktrace = require("stack-trace"),
    fs = require('fs'),
    Promise = require('promise'),
    Utils = require("./utils"),
    Configuration = require("./configuration");

function loadCode (error, callback) {
    var padding = 4; // Padding of either side of the frame's line (4 actually means 3...)

    Promise.all(error.stacktrace.map(function (frame) {
        if (frame.inProject) {
            return new Promise(function (resolve) {
                fs.readFile(frame.path, function (err, data) {
                    if ( ! err) {
                        var fileLines = data.toString().split('\n');

                        frame.code = {};

                        for (var i = frame.lineNumber - padding; i < (frame.lineNumber - 1) + padding; i++) {
                            if (typeof fileLines[i] === 'string') {
                                frame.code[i + 1] = fileLines[i];
                            }
                        }
                    }

                    resolve();
                });
            });
        }
    })).then(callback);
}

function processCallSites (callSites) {
    return callSites.map(function(callSite) {
        var frame, _ref;
        frame = {
           file: callSite.getFileName(),
           path: callSite.getFileName(),
           method: callSite.getMethodName() || callSite.getFunctionName() || "none",
           lineNumber: callSite.getLineNumber(),
           columnNumber: callSite.getColumnNumber()
        };

        if (Configuration.projectRoot && frame.file && frame.file.indexOf(Configuration.projectRoot) === 0) {
            frame.inProject = callSite.getFileName().indexOf("node_modules") === -1;
            if (!frame.inProject) {
                delete frame.inProject;
            }
            frame.file = frame.file.substr(Configuration.projectRoot.length + 1);
        }
        return frame;
    });
}

function Error(error, errorClass) {
    var callSites;
    if (Utils.typeOf(error) === "string") {
        this.message = error;
        this.errorClass = errorClass || "Error";
    } else if (error) {
        this.message = error.message;
        this.errorClass = errorClass || error.name || error.constructor.name || "Error";
    } else {
        this.message = "[unknown]";
        this.errorClass = errorClass || "Error";
    }
    callSites = stacktrace.parse(error);
    if (callSites.length === 0) {
        callSites = stacktrace.get();
    }
    this.stacktrace = processCallSites(callSites, Configuration.projectRoot);
}

Error.prototype.loadCode = function (callback) {
    loadCode(this, callback);
};

Error.buildErrors = function(error, errorClass) {
    var returnArray;
    returnArray = [new module.exports(error, errorClass)];
    if (error.oauthError) {
        returnArray.push(new module.exports(error.oauthError));
    }
    return returnArray;
};

module.exports = Error;
<|endoftext|>


<commit_before>'use strict';

var program = require('commander'),
    _ = require('lodash'),
    fs = require('fs'),
    async = require('async'),
    scan = require('./scan');

function run(program, source) {
    var paths = source.split(',').map(function(path) {
        return path + '/**/*';
    });

    async.map(paths, scan, function(err, results) {
        var strings = results.reduce(function(strings, result) {
            return strings.concat(result);
        }, []);

        var existing = {};
        try {
          existing = JSON.parse(fs.readFileSync(program.output));
        } catch(err) {
          // nothing
        }

        strings = _.uniq(strings).sort().reduce(function(strings, string) {
            if(string in existing) {
              strings[string] = existing[string];
            } else {
              strings[string] = string;
            }
            return strings;
        }, {});

        if (program.output) return fs.writeFile(program.output, JSON.stringify(strings, null, 2), process.exit);

        console.log(JSON.stringify(strings, null, 2));

        return process.exit();
    });
}

program
    .command('*')
    .description('Scans for gettext functions __("literal") and __n("single", "plural", n )')
    .action(function(source) {
        run(program, source);
    });

program
    .version('0.0.1')
    .option('-o, --output [file]', 'Output file', String)
    .parse(process.argv);

if (program.args.length === 0) program.help();
<commit_msg>rewrite cli with yargs<commit_after>#! /usr/bin/env node

const yargs = require("yargs");
const fs = require("fs");
const scan = require("../lib/scan");

function main() {
    const argv = yargs
        .usage("Usage: $0 -o [output] -x [exclude]")
        .describe("o", "Output file")
        .alias("o", "output")
        .describe("x", "Exclude pattern")
        .alias("x", "exclude")
        .demandCommand(1)
        .argv;

    let files = argv._;

    if (argv.exclude) {
        let re = new RegExp(argv.exclude);
        files = files.filter(v => !re.test(v));
    }

    const strings = scan(files);

    if (argv.output) {
        fs.writeFile(argv.output, JSON.stringify(strings, null, 2), process.exit);
        return;
    }
    console.log(JSON.stringify(strings, null, 2));
}

main();<|endoftext|>


<commit_before>'use strict';

const assert = require('assert');
const ActiveCollab = require('../index');
const fs = require('fs');
const chai = require('chai');
const expect = chai.expect;
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);

if (!process.env.email || !process.env.password) {
    throw 'Expected an email and password in process.env';
}

const client = new ActiveCollab.Client('ActiveCollab.js', 'Gerard');

describe('Client', function () {
    describe('#login()', function () {
        it('should login', () => client.login(process.env.email, process.env.password));
    });
});

describe('Account', function () {
    let account;

    before(() => {
        if (client.accounts.empty()) {
            throw 'No accounts are loaded.'
        }

        account = client.accounts.first();
    });

    describe('#issueToken()', function () {
        it('should fetch the token', () => account.issueToken());
    });

    describe('#projects()', function () {
        it('should get the projects', () => account.projects());
    });

    describe('#projects.add()', function () {
        it('should fail because the name is missing', () => expect(account.projects.add({})).to.be.rejected);
        it('should add a new Test project', () => expect(account.projects.add({name: 'Test'})).to.be.fulfilled);
    });
});

describe('Project', function () {
    let account;

    before(() => {
        if (client.accounts.empty()) {
            throw 'No accounts are loaded.'
        }

        account = client.accounts.first();
    });

    describe('#remove()', function () {
        it('should remove the Test project(s)', () => {
            return expect(account.projects()
                .then((projects) => projects
                    .filter((project) => project.name === 'Test')
                    .promise((project) => project.remove())
                )
            ).to.be.fulfilled
        });
    });
});<commit_msg>Fixed testing after structure change<commit_after>'use strict';

const assert = require('assert');
const ActiveCollab = require('../index');
const fs = require('fs');
const chai = require('chai');
const expect = chai.expect;
const chaiAsPromised = require("chai-as-promised");
chai.use(chaiAsPromised);

if (!process.env.email || !process.env.password) {
    throw 'Expected an email and password in process.env';
}

const client = new ActiveCollab.Client('ActiveCollab.js', 'Gerard');

describe('Client', function () {
    describe('#login()', function () {
        it('should login', () => client.login(process.env.email, process.env.password));
    });
});

describe('Account', function () {
    let account;

    before(() => {
        if (client.accounts.empty()) {
            throw 'No accounts are loaded.'
        }

        account = client.accounts.first();
    });

    describe('#issueToken()', function () {
        it('should fetch the token', () => account.issueToken());
    });

    describe('#projects()', function () {
        it('should get the projects', () => account.projects());
    });

    describe('#projects.add()', function () {
        it('should fail because the name is missing', () => expect(account.projects.create({})).to.be.rejected);
        it('should add a new Test project', () => expect(account.projects.create({name: 'Test'})).to.be.fulfilled);
    });
});

describe('Project', function () {
    let account;

    before(() => {
        if (client.accounts.empty()) {
            throw 'No accounts are loaded.'
        }

        account = client.accounts.first();
    });

    describe('#remove()', function () {
        it('should remove the Test project(s)', () => {
            return expect(account.projects()
                .then((projects) => projects
                    .filter((project) => project.name === 'Test')
                    .promise((project) => project.remove())
                )
            ).to.be.fulfilled
        });
    });
});<|endoftext|>


<commit_before>var inquirer = require('inquirer');
var yeomanEnv = require('yeoman-environment');

module.exports = {debugFunction: debug}
process.stdin.setEncoding('utf8');
var prompts = require('./prompts');

var debugSupport = true;
var args = process.argv.slice(2);

if (!args[0]) {
  process.exit(1);
}

var isPlainText = args[0] == '--plain';
var generatorName = args[isPlainText ? 1 : 0];

var env = yeomanEnv.createEnv(['nameValue'], null, new Adapter);
var inputHandler = (function () {
  return {
    waitInput: function (getResultFunction, question, callback) {
      process.stdin.removeAllListeners('readable');
      var listener;

      function drop() {
        if (listener) {
          process.stdin.removeListener('readable', listener);
        }
      }

      listener = function (v) {
        var chunk = process.stdin.read();
        debug('event ' + chunk);
        if (chunk || chunk == '\n') {
          debug('read chunk ' + chunk);
          drop();
          callback(getResultFunction(question, chunk));
        }
      };

      process.stdin.on('readable', listener);
    }
  }
})();

//will works without hacks in the next versions
env.lookup(function () {
  env.run(generatorName, null, function () {
    debug('done run generator');
  });
});

/**
 * @constructor
 */
function Adapter() {
  debug('adapter create');
  this.prompt = inquirer.createPromptModule();

  Object.keys(this.prompt.prompts).forEach(function (name) {
    this.prompt.registerPrompt(name, Prompt.bind(null, prompts.handlers[name] || prompts.defaultHandler));
  }, this);

  this.log = yeomanEnv.util.log();

}

/**
 * @constructor
 */
function Prompt(getResultFunction, question, rl, answers) {
  this.question = question;
  this.rl = rl;
  this.answers = answers;

  this.run = function (callback) {
    printMessage(question);

    inputHandler.waitInput(getResultFunction, question, callback);
  }

  return this;
}

function printMessage(questionWithAnswers) {
  if (isPlainText) {
    //print plain text
    process.stdout.write(questionWithAnswers.message + '\n');
    if (questionWithAnswers.choices) {
      var index = 0;
      questionWithAnswers.choices.forEach(function (v) {
        if (v instanceof String) {
          process.stdout.write('' + (index++) + ' ' + v + '\n')
        } else {
          process.stdout.write('' + (index++) + ' ' + (v.name || v.value) + (v.checked ? ' x' : '') + '\n');
        }
      });
    }
  } else {
    var stringAnswers = JSON.stringify(questionWithAnswers);
    debug(stringAnswers);
    process.stdout.write(stringAnswers + '\n');
  }
}

function debug(v) {
  if (debugSupport) console.log('--debug ' + v);
}
<commit_msg>current state: command line arguments added<commit_after>var path = require('path');

module.exports = {debugFunction: debug}
process.stdin.setEncoding('utf8');
var prompts = require('./prompts');

var debugSupport = true;

function parseArgs(args) {
  var options = {}

  for (var i = 0; i < args.length; i++) {
    switch (args[i]) {
      case "--generatorPath":
      {
        options.generatorPath = (++i < args.length) ? args[i] : null;
        break;
      }
      case "--generatorName":
      {
        options.generatorName = (++i < args.length) ? args[i] : null;
        break;
      }
      case "--yeoman":
      {
        options.yeoman = (++i < args.length) ? args[i] : null;
        break;
      }
      case "--arguments":
      {
        options.arguments = (++i < args.length) ? args[i].split(' ') : null;
        break;
      }
      case "--plain":
      {
        options.plain = true;
        break;
      }
      default: //do nothing
    }
  }

  return options;
}

var options = parseArgs(process.argv.slice(2));

var prefix = (options.yeoman ? options.yeoman + path.sep + 'node_modules' + path.sep : '');
var inquirer = require(prefix + 'inquirer');
var yeomanEnv = require(prefix + 'yeoman-environment');

var env = yeomanEnv.createEnv(options.arguments, null, new Adapter);
var inputHandler = (function () {
  return {
    waitInput: function (getResultFunction, question, callback) {
      debug("start waiting");
      //process.stdin.removeAllListeners('readable');

      debug( require('events').EventEmitter.listenerCount(process, 'readable'));

      var listener;

      function drop() {
        if (listener) {
          debug("remove listener");
          process.stdin.removeListener('readable', listener);
        }
      }

      listener = function (v) {
        var chunk = process.stdin.read();
        debug('event ' + chunk);
        if (chunk || chunk == '\n') {
          debug('read chunk ' + chunk);
          drop();
          callback(getResultFunction(question, chunk));
        }
      };

      debug("add listener");
      process.stdin.on('readable', listener);
    }
  }
})();

//will works without hacks in the next versions
var callbackDone = function () {
  debug('done run generator');
}
if (options.generatorName) {
  env.lookup(function () {
    env.run(generatorName, null, callbackDone);
  });
} else if (options.generatorPath) {
  env.register(options.generatorPath, 'generator');
  env.run('generator', callbackDone);
} else {
  throw new Error("You should specify generatorName or generatorPath");
}
/**
 * @constructor
 */
function Adapter() {
  debug('adapter create');
  this.prompt = inquirer.createPromptModule();

  Object.keys(this.prompt.prompts).forEach(function (name) {
    this.prompt.registerPrompt(name, Prompt.bind(null, prompts.handlers[name] || prompts.defaultHandler));
  }, this);

  this.log = yeomanEnv.util.log();

}

/**
 * @constructor
 */
function Prompt(getResultFunction, question, rl, answers) {
  this.question = question;
  this.rl = rl;
  this.answers = answers;

  this.run = function (callback) {
    printMessage(question);

    inputHandler.waitInput(getResultFunction, question, callback);
  }

  return this;
}

function printMessage(questionWithAnswers) {
  if (options.plain) {
    //print plain text
    process.stdout.write(questionWithAnswers.message + '\n');
    if (questionWithAnswers.choices) {
      var index = 0;
      questionWithAnswers.choices.forEach(function (v) {
        if (v instanceof String) {
          process.stdout.write('' + (index++) + ' ' + v + '\n')
        } else {
          process.stdout.write('' + (index++) + ' ' + (v.name || v.value) + (v.checked ? ' x' : '') + '\n');
        }
      });
    }
  } else {
    var stringAnswers = JSON.stringify(questionWithAnswers);
    debug(stringAnswers);
    process.stdout.write(stringAnswers + '\n');
  }
}

function debug(v) {
  if (debugSupport) console.log('--debug ' + v);
}
<|endoftext|>


<commit_before>const rpc = require('../index.js')('amqp://yroncwgv:bQm23UIjEmgKf7Chs5oNji2rFpKDF4sB@puma.rmq.cloudamqp.com/yroncwgv');
const assert = require('chai').assert;

describe('Create remote action', function() {
    it('Should create remote action "sum" and return promise with answer', function() {
        rpc.handle('sum', function (a, b) {
            return Promise.resolve(a+b);
        })
    });
});

describe('call remote action', function() {
    it('Call remote action "sum" with 2+3 and return 5 with callback', function() {
        return new Promise(function (resolve, reject) {
            rpc.call('sum', 2, 3, function (err, result) {
                if(err) return reject(err);
                assert.equal(result, 2+3);
                resolve()
            })
        });
    });
    it('Call remote action "sum" with 2+3 and return 5 with promise', function() {
        return new Promise( (resolve, reject) => {
            rpc.promise('sum', 2, 3).then( function (result) {
                assert.equal(result, 2+3);
                resolve();
            })
                .catch(reject)
        })
    });
});<commit_msg>Improve test<commit_after>const rpc = require('../index.js')('amqp://yroncwgv:bQm23UIjEmgKf7Chs5oNji2rFpKDF4sB@puma.rmq.cloudamqp.com/yroncwgv');
const assert = require('chai').assert;

describe('Create remote action', function() {
    it('Should create remote action "sum" and return promise with answer', function() {
        rpc.handle('square', function (a) {
            if(isNaN(+a)) return Promise.reject(new Error('Argument is NaN'));
            return Promise.resolve(a*a);
        })
    });
});

describe('call remote action', function() {
    it('Call remote action "square" with 2 and expect 4 with callback', function() {
        return new Promise(function (resolve, reject) {
            rpc.call('square', 2, function (err, result) {
                if(err) return reject(err);
                assert.equal(result, 2*2);
                resolve()
            })
        });
    });
    it('Call remote action "square" with 2 and expect 4 with promise', function() {
        return new Promise( (resolve, reject) => {
            rpc.promise('square', 2).then( function (result) {
                assert.equal(result, 2*2);
                resolve();
            })
                .catch(reject)
        })
    });
    it('Call remote action "square" with "string" and expect rejection', function() {
        return new Promise( (resolve, reject) => {
            rpc.promise('square', 'string')
                .then(reject)
                .catch(resolve)
        })
    });
});<|endoftext|>


<commit_before>'use strict';
const path = require('path');
const fs = require('fs');
const del = require('del');
const updateNotifier = require('update-notifier');
const figures = require('figures');
const arrify = require('arrify');
const yargs = require('yargs');
const Promise = require('bluebird');
const isCi = require('is-ci');
const loadConfig = require('./load-config');

// Bluebird specific
Promise.longStackTraces();

function exit(message) {
	console.error(`\n  ${require('./chalk').get().red(figures.cross)} ${message}`);
	process.exit(1); // eslint-disable-line unicorn/no-process-exit
}

const coerceLastValue = value => {
	return Array.isArray(value) ? value.pop() : value;
};

const FLAGS = {
	concurrency: {
		alias: 'c',
		coerce: coerceLastValue,
		description: 'Max number of test files running at the same time (Default: CPU cores)',
		type: 'number'
	},
	'fail-fast': {
		coerce: coerceLastValue,
		description: 'Stop after first test failure',
		type: 'boolean'
	},
	match: {
		alias: 'm',
		description: 'Only run tests with matching title (Can be repeated)',
		type: 'string'
	},
	serial: {
		alias: 's',
		coerce: coerceLastValue,
		description: 'Run tests serially',
		type: 'boolean'
	},
	tap: {
		alias: 't',
		coerce: coerceLastValue,
		description: 'Generate TAP output',
		type: 'boolean'
	},
	timeout: {
		alias: 'T',
		coerce: coerceLastValue,
		description: 'Set global timeout (milliseconds or human-readable, e.g. 10s, 2m)',
		type: 'string'
	},
	'update-snapshots': {
		alias: 'u',
		coerce: coerceLastValue,
		description: 'Update snapshots',
		type: 'boolean'
	},
	verbose: {
		alias: 'v',
		coerce: coerceLastValue,
		description: 'Enable verbose output',
		type: 'boolean'
	},
	watch: {
		alias: 'w',
		coerce: coerceLastValue,
		description: 'Re-run tests when files change',
		type: 'boolean'
	}
};

exports.run = async () => { // eslint-disable-line complexity
	let conf = {};
	let confError = null;
	try {
		const {argv: {config: configFile}} = yargs.help(false);
		conf = loadConfig({configFile});
	} catch (error) {
		confError = error;
	}

	let debug = null;
	let resetCache = false;
	const {argv} = yargs
		.parserConfiguration({
			'boolean-negation': true,
			'camel-case-expansion': false,
			'combine-arrays': false,
			'dot-notation': false,
			'duplicate-arguments-array': true,
			'flatten-duplicate-arrays': true,
			'negation-prefix': 'no-',
			'parse-numbers': true,
			'populate--': true,
			'set-placeholder-key': false,
			'short-option-groups': true,
			'strip-aliased': true,
			'unknown-options-as-args': false
		})
		.usage('$0 <files>')
		.usage('$0 debug <file>')
		.usage('$0 reset-cache')
		.options({
			color: {
				description: 'Force color output',
				type: 'boolean'
			},
			config: {
				description: 'JavaScript file for AVA to read its config from, instead of using package.json or ava.config.js files'
			}
		})
		.command('*', 'Run tests', yargs => yargs.options(FLAGS).positional('files', {
			array: true,
			describe: 'Paths to individual test files. Leave empty if you want AVA to search for files instead.',
			type: 'string'
		}))
		.command(
			'debug',
			'Activate Node.js inspector and run the test file',
			yargs => yargs.options(FLAGS).options({
				break: {
					description: 'Break before the test file is loaded',
					type: 'boolean'
				},
				port: {
					default: 9229,
					description: 'Port on which you can connect to the inspector',
					type: 'number'
				}
			}).positional('file', {
				demand: true,
				describe: 'Path to an individual test file',
				type: 'string'
			}),
			argv => {
				debug = {
					break: argv.break === true,
					files: argv._.slice(1),
					port: argv.port
				};
			})
		.command(
			'reset-cache',
			'Reset AVA\'s compilation cache and exit',
			yargs => yargs,
			() => {
				resetCache = true;
			})
		.example('$0')
		.example('$0 test.js')
		.help();

	const chalk = require('./chalk').set({level: argv.color ? 1 : 0});

	if (confError) {
		if (confError.parent) {
			exit(`${confError.message}\n\n${chalk.gray((confError.parent && confError.parent.stack) || confError.parent)}`);
		} else {
			exit(confError.message);
		}
	}

	updateNotifier({pkg: require('../package.json')}).notify();

	const {nonSemVerExperiments: experiments, projectDir} = conf;
	if (resetCache) {
		const cacheDir = path.join(projectDir, 'node_modules', '.cache', 'ava');
		try {
			await del('*', {
				cwd: cacheDir,
				nodir: true
			});
			console.error(`\n${chalk.green(figures.tick)} Removed AVA cache files in ${cacheDir}`);
			process.exit(0); // eslint-disable-line unicorn/no-process-exit
		} catch (error) {
			exit(`Error removing AVA cache files in ${cacheDir}\n\n${chalk.gray((error && error.stack) || error)}`);
		}

		return;
	}

	if (argv.watch) {
		if (argv.tap && !conf.tap) {
			exit('The TAP reporter is not available when using watch mode.');
		}

		if (isCi) {
			exit('Watch mode is not available in CI, as it prevents AVA from terminating.');
		}

		if (debug !== null) {
			exit('Watch mode is not available when debugging.');
		}
	}

	if (debug !== null) {
		if (argv.tap && !conf.tap) {
			exit('The TAP reporter is not available when debugging.');
		}

		if (isCi) {
			exit('Debugging is not available in CI.');
		}
	}

	const combined = {...conf};
	for (const flag of Object.keys(FLAGS)) {
		if (Reflect.has(argv, flag)) {
			if (flag === 'fail-fast') {
				combined.failFast = argv[flag];
			} else if (flag === 'update-snapshots') {
				combined.updateSnaphots = argv[flag];
			} else {
				combined[flag] = argv[flag];
			}
		}
	}

	if (Reflect.has(combined, 'concurrency') && (!Number.isInteger(combined.concurrency) || combined.concurrency < 0)) {
		exit('The --concurrency or -c flag must be provided with a nonnegative integer.');
	}

	if (!combined.tap && Object.keys(experiments).length > 0) {
		console.log(chalk.magenta(`  ${figures.warning} Experiments are enabled. These are unsupported and may change or be removed at any time.`));
	}

	if (Reflect.has(conf, 'compileEnhancements')) {
		exit('Enhancement compilation must be configured in AVA’s Babel options.');
	}

	if (Reflect.has(conf, 'helpers')) {
		exit('AVA no longer compiles helpers. Add exclusion patterns to the \'files\' configuration and specify \'compileAsTests\' in the Babel options instead.');
	}

	if (Reflect.has(conf, 'sources')) {
		exit('\'sources\' has been removed. Use \'ignoredByWatcher\' to provide glob patterns of files that the watcher should ignore.');
	}

	const ciParallelVars = require('ci-parallel-vars');
	const Api = require('./api');
	const VerboseReporter = require('./reporters/verbose');
	const MiniReporter = require('./reporters/mini');
	const TapReporter = require('./reporters/tap');
	const Watcher = require('./watcher');
	const babelManager = require('./babel-manager');
	const normalizeExtensions = require('./extensions');
	const {normalizeGlobs} = require('./globs');
	const validateEnvironmentVariables = require('./environment-variables');

	let babelProvider;
	if (Reflect.has(conf, 'babel')) {
		try {
			babelProvider = babelManager({projectDir}).main({config: conf.babel});
		} catch (error) {
			exit(error.message);
		}
	}

	let environmentVariables;
	try {
		environmentVariables = validateEnvironmentVariables(conf.environmentVariables);
	} catch (error) {
		exit(error.message);
	}

	let extensions;
	try {
		extensions = normalizeExtensions(conf.extensions, babelProvider);
	} catch (error) {
		exit(error.message);
	}

	let globs;
	try {
		globs = normalizeGlobs({files: conf.files, ignoredByWatcher: conf.ignoredByWatcher, extensions});
	} catch (error) {
		exit(error.message);
	}

	let parallelRuns = null;
	if (isCi && ciParallelVars) {
		const {index: currentIndex, total: totalRuns} = ciParallelVars;
		parallelRuns = {currentIndex, totalRuns};
	}

	const match = combined.match === '' ? [] : arrify(combined.match);

	const input = debug ? debug.files : argv._;
	const resolveTestsFrom = input.length === 0 ? projectDir : process.cwd();
	const files = input.map(file => path.relative(resolveTestsFrom, path.resolve(process.cwd(), file)));

	for (const file of files) {
		try {
			const stats = fs.statSync(file);
			if (!stats.isFile()) {
				exit(`${file} is not a test file.`);
			}
		} catch (error) {
			if (error.code === 'ENOENT') {
				exit(`${file} does not exist.`);
			} else {
				exit(`Error accessing ${file}\n\n${chalk.gray((error && error.stack) || error)}`);
			}
		}
	}

	if (debug !== null && files.length !== 1) {
		exit('Provide the path to the test file you wish to debug');
	}

	const api = new Api({
		babelProvider,
		cacheEnabled: combined.cache !== false,
		color: combined.color,
		concurrency: combined.concurrency || 0,
		debug,
		experiments,
		extensions,
		failFast: combined.failFast,
		failWithoutAssertions: combined.failWithoutAssertions !== false,
		globs,
		environmentVariables,
		match,
		parallelRuns,
		projectDir,
		ranFromCli: true,
		require: arrify(combined.require),
		resolveTestsFrom,
		serial: combined.serial,
		snapshotDir: combined.snapshotDir ? path.resolve(projectDir, combined.snapshotDir) : null,
		timeout: combined.timeout,
		updateSnapshots: combined.updateSnapshots,
		workerArgv: argv['--']
	});

	let reporter;
	if (combined.tap && !combined.watch && debug === null) {
		reporter = new TapReporter({
			reportStream: process.stdout,
			stdStream: process.stderr
		});
	} else if (debug !== null || combined.verbose || isCi || !process.stdout.isTTY) {
		reporter = new VerboseReporter({
			reportStream: process.stdout,
			stdStream: process.stderr,
			watching: combined.watch
		});
	} else {
		reporter = new MiniReporter({
			reportStream: process.stdout,
			stdStream: process.stderr,
			watching: combined.watch
		});
	}

	api.on('run', plan => {
		reporter.startRun(plan);

		plan.status.on('stateChange', evt => {
			if (evt.type === 'interrupt') {
				reporter.endRun();
				process.exit(1); // eslint-disable-line unicorn/no-process-exit
			}
		});
	});

	if (combined.watch) {
		const watcher = new Watcher({
			api,
			reporter,
			files,
			globs,
			projectDir,
			babelProvider
		});
		watcher.observeStdin(process.stdin);
	} else {
		const runStatus = await api.run(files);
		process.exitCode = runStatus.suggestExitCode({matching: match.length > 0});
		reporter.endRun();
	}
};
<commit_msg>Fix color initialization<commit_after>'use strict';
const path = require('path');
const fs = require('fs');
const del = require('del');
const updateNotifier = require('update-notifier');
const figures = require('figures');
const arrify = require('arrify');
const yargs = require('yargs');
const Promise = require('bluebird');
const isCi = require('is-ci');
const loadConfig = require('./load-config');

// Bluebird specific
Promise.longStackTraces();

function exit(message) {
	console.error(`\n  ${require('./chalk').get().red(figures.cross)} ${message}`);
	process.exit(1); // eslint-disable-line unicorn/no-process-exit
}

const coerceLastValue = value => {
	return Array.isArray(value) ? value.pop() : value;
};

const FLAGS = {
	concurrency: {
		alias: 'c',
		coerce: coerceLastValue,
		description: 'Max number of test files running at the same time (Default: CPU cores)',
		type: 'number'
	},
	'fail-fast': {
		coerce: coerceLastValue,
		description: 'Stop after first test failure',
		type: 'boolean'
	},
	match: {
		alias: 'm',
		description: 'Only run tests with matching title (Can be repeated)',
		type: 'string'
	},
	serial: {
		alias: 's',
		coerce: coerceLastValue,
		description: 'Run tests serially',
		type: 'boolean'
	},
	tap: {
		alias: 't',
		coerce: coerceLastValue,
		description: 'Generate TAP output',
		type: 'boolean'
	},
	timeout: {
		alias: 'T',
		coerce: coerceLastValue,
		description: 'Set global timeout (milliseconds or human-readable, e.g. 10s, 2m)',
		type: 'string'
	},
	'update-snapshots': {
		alias: 'u',
		coerce: coerceLastValue,
		description: 'Update snapshots',
		type: 'boolean'
	},
	verbose: {
		alias: 'v',
		coerce: coerceLastValue,
		description: 'Enable verbose output',
		type: 'boolean'
	},
	watch: {
		alias: 'w',
		coerce: coerceLastValue,
		description: 'Re-run tests when files change',
		type: 'boolean'
	}
};

exports.run = async () => { // eslint-disable-line complexity
	let conf = {};
	let confError = null;
	try {
		const {argv: {config: configFile}} = yargs.help(false);
		conf = loadConfig({configFile});
	} catch (error) {
		confError = error;
	}

	let debug = null;
	let resetCache = false;
	const {argv} = yargs
		.parserConfiguration({
			'boolean-negation': true,
			'camel-case-expansion': false,
			'combine-arrays': false,
			'dot-notation': false,
			'duplicate-arguments-array': true,
			'flatten-duplicate-arrays': true,
			'negation-prefix': 'no-',
			'parse-numbers': true,
			'populate--': true,
			'set-placeholder-key': false,
			'short-option-groups': true,
			'strip-aliased': true,
			'unknown-options-as-args': false
		})
		.usage('$0 <files>')
		.usage('$0 debug <file>')
		.usage('$0 reset-cache')
		.options({
			color: {
				description: 'Force color output',
				type: 'boolean'
			},
			config: {
				description: 'JavaScript file for AVA to read its config from, instead of using package.json or ava.config.js files'
			}
		})
		.command('*', 'Run tests', yargs => yargs.options(FLAGS).positional('files', {
			array: true,
			describe: 'Paths to individual test files. Leave empty if you want AVA to search for files instead.',
			type: 'string'
		}))
		.command(
			'debug',
			'Activate Node.js inspector and run the test file',
			yargs => yargs.options(FLAGS).options({
				break: {
					description: 'Break before the test file is loaded',
					type: 'boolean'
				},
				port: {
					default: 9229,
					description: 'Port on which you can connect to the inspector',
					type: 'number'
				}
			}).positional('file', {
				demand: true,
				describe: 'Path to an individual test file',
				type: 'string'
			}),
			argv => {
				debug = {
					break: argv.break === true,
					files: argv._.slice(1),
					port: argv.port
				};
			})
		.command(
			'reset-cache',
			'Reset AVA\'s compilation cache and exit',
			yargs => yargs,
			() => {
				resetCache = true;
			})
		.example('$0')
		.example('$0 test.js')
		.help();

	const combined = {...conf};
	for (const flag of Object.keys(FLAGS)) {
		if (Reflect.has(argv, flag)) {
			if (flag === 'fail-fast') {
				combined.failFast = argv[flag];
			} else if (flag === 'update-snapshots') {
				combined.updateSnaphots = argv[flag];
			} else {
				combined[flag] = argv[flag];
			}
		}
	}

	const chalk = require('./chalk').set({level: combined.color === false ? 0 : require('chalk').level});

	if (confError) {
		if (confError.parent) {
			exit(`${confError.message}\n\n${chalk.gray((confError.parent && confError.parent.stack) || confError.parent)}`);
		} else {
			exit(confError.message);
		}
	}

	updateNotifier({pkg: require('../package.json')}).notify();

	const {nonSemVerExperiments: experiments, projectDir} = conf;
	if (resetCache) {
		const cacheDir = path.join(projectDir, 'node_modules', '.cache', 'ava');
		try {
			await del('*', {
				cwd: cacheDir,
				nodir: true
			});
			console.error(`\n${chalk.green(figures.tick)} Removed AVA cache files in ${cacheDir}`);
			process.exit(0); // eslint-disable-line unicorn/no-process-exit
		} catch (error) {
			exit(`Error removing AVA cache files in ${cacheDir}\n\n${chalk.gray((error && error.stack) || error)}`);
		}

		return;
	}

	if (argv.watch) {
		if (argv.tap && !conf.tap) {
			exit('The TAP reporter is not available when using watch mode.');
		}

		if (isCi) {
			exit('Watch mode is not available in CI, as it prevents AVA from terminating.');
		}

		if (debug !== null) {
			exit('Watch mode is not available when debugging.');
		}
	}

	if (debug !== null) {
		if (argv.tap && !conf.tap) {
			exit('The TAP reporter is not available when debugging.');
		}

		if (isCi) {
			exit('Debugging is not available in CI.');
		}
	}

	if (Reflect.has(combined, 'concurrency') && (!Number.isInteger(combined.concurrency) || combined.concurrency < 0)) {
		exit('The --concurrency or -c flag must be provided with a nonnegative integer.');
	}

	if (!combined.tap && Object.keys(experiments).length > 0) {
		console.log(chalk.magenta(`  ${figures.warning} Experiments are enabled. These are unsupported and may change or be removed at any time.`));
	}

	if (Reflect.has(conf, 'compileEnhancements')) {
		exit('Enhancement compilation must be configured in AVA’s Babel options.');
	}

	if (Reflect.has(conf, 'helpers')) {
		exit('AVA no longer compiles helpers. Add exclusion patterns to the \'files\' configuration and specify \'compileAsTests\' in the Babel options instead.');
	}

	if (Reflect.has(conf, 'sources')) {
		exit('\'sources\' has been removed. Use \'ignoredByWatcher\' to provide glob patterns of files that the watcher should ignore.');
	}

	const ciParallelVars = require('ci-parallel-vars');
	const Api = require('./api');
	const VerboseReporter = require('./reporters/verbose');
	const MiniReporter = require('./reporters/mini');
	const TapReporter = require('./reporters/tap');
	const Watcher = require('./watcher');
	const babelManager = require('./babel-manager');
	const normalizeExtensions = require('./extensions');
	const {normalizeGlobs} = require('./globs');
	const validateEnvironmentVariables = require('./environment-variables');

	let babelProvider;
	if (Reflect.has(conf, 'babel')) {
		try {
			babelProvider = babelManager({projectDir}).main({config: conf.babel});
		} catch (error) {
			exit(error.message);
		}
	}

	let environmentVariables;
	try {
		environmentVariables = validateEnvironmentVariables(conf.environmentVariables);
	} catch (error) {
		exit(error.message);
	}

	let extensions;
	try {
		extensions = normalizeExtensions(conf.extensions, babelProvider);
	} catch (error) {
		exit(error.message);
	}

	let globs;
	try {
		globs = normalizeGlobs({files: conf.files, ignoredByWatcher: conf.ignoredByWatcher, extensions});
	} catch (error) {
		exit(error.message);
	}

	let parallelRuns = null;
	if (isCi && ciParallelVars) {
		const {index: currentIndex, total: totalRuns} = ciParallelVars;
		parallelRuns = {currentIndex, totalRuns};
	}

	const match = combined.match === '' ? [] : arrify(combined.match);

	const input = debug ? debug.files : argv._;
	const resolveTestsFrom = input.length === 0 ? projectDir : process.cwd();
	const files = input.map(file => path.relative(resolveTestsFrom, path.resolve(process.cwd(), file)));

	for (const file of files) {
		try {
			const stats = fs.statSync(file);
			if (!stats.isFile()) {
				exit(`${file} is not a test file.`);
			}
		} catch (error) {
			if (error.code === 'ENOENT') {
				exit(`${file} does not exist.`);
			} else {
				exit(`Error accessing ${file}\n\n${chalk.gray((error && error.stack) || error)}`);
			}
		}
	}

	if (debug !== null && files.length !== 1) {
		exit('Provide the path to the test file you wish to debug');
	}

	const api = new Api({
		babelProvider,
		cacheEnabled: combined.cache !== false,
		color: combined.color,
		concurrency: combined.concurrency || 0,
		debug,
		experiments,
		extensions,
		failFast: combined.failFast,
		failWithoutAssertions: combined.failWithoutAssertions !== false,
		globs,
		environmentVariables,
		match,
		parallelRuns,
		projectDir,
		ranFromCli: true,
		require: arrify(combined.require),
		resolveTestsFrom,
		serial: combined.serial,
		snapshotDir: combined.snapshotDir ? path.resolve(projectDir, combined.snapshotDir) : null,
		timeout: combined.timeout,
		updateSnapshots: combined.updateSnapshots,
		workerArgv: argv['--']
	});

	let reporter;
	if (combined.tap && !combined.watch && debug === null) {
		reporter = new TapReporter({
			reportStream: process.stdout,
			stdStream: process.stderr
		});
	} else if (debug !== null || combined.verbose || isCi || !process.stdout.isTTY) {
		reporter = new VerboseReporter({
			reportStream: process.stdout,
			stdStream: process.stderr,
			watching: combined.watch
		});
	} else {
		reporter = new MiniReporter({
			reportStream: process.stdout,
			stdStream: process.stderr,
			watching: combined.watch
		});
	}

	api.on('run', plan => {
		reporter.startRun(plan);

		plan.status.on('stateChange', evt => {
			if (evt.type === 'interrupt') {
				reporter.endRun();
				process.exit(1); // eslint-disable-line unicorn/no-process-exit
			}
		});
	});

	if (combined.watch) {
		const watcher = new Watcher({
			api,
			reporter,
			files,
			globs,
			projectDir,
			babelProvider
		});
		watcher.observeStdin(process.stdin);
	} else {
		const runStatus = await api.run(files);
		process.exitCode = runStatus.suggestExitCode({matching: match.length > 0});
		reporter.endRun();
	}
};
<|endoftext|>


<commit_before>import {on, off, fire} from '../delegated-events';

describe('delegated event listeners', function() {
  describe('firing custom events', function() {
    it('fires custom events with detail', function() {
      const observer = function(event) {
        assert(event.bubbles);
        assert(event.cancelable);
        assert.equal(event.type, 'test:detail');
        assert.deepEqual(event.detail, {id: 42, login: 'hubot'});
        assert.strictEqual(document.body, event.target);
        assert.instanceOf(event, CustomEvent);
      };
      document.addEventListener('test:detail', observer);
      fire(document.body, 'test:detail', {id: 42, login: 'hubot'});
      document.removeEventListener('test:detail', observer);
    });

    it('fires custom events without detail', function() {
      const observer = function(event) {
        assert(event.detail === undefined || event.detail === null);
        assert.instanceOf(event, CustomEvent);
      };
      document.addEventListener('test:fire', observer);
      fire(document.body, 'test:fire');
      document.removeEventListener('test:fire', observer);
    });

    it('returns canceled when default prevented', function() {
      const observer = (event) => event.preventDefault();
      document.addEventListener('test:cancel', observer);
      const canceled = !fire(document.body, 'test:cancel');
      assert.equal(canceled, true);
      document.removeEventListener('test:cancel', observer);
    });

    it('returns not canceled when default is not prevented', function() {
      const observer = (event) => assert.ok(event);
      document.addEventListener('test:event', observer);
      const canceled = !fire(document.body, 'test:event');
      assert.equal(canceled, false);
      document.removeEventListener('test:event', observer);
    });
  });

  describe('registering event observers', function() {
    it('observes custom events', function() {
      const observer = function(event) {
        assert(event.bubbles);
        assert(event.cancelable);
        assert.equal(event.type, 'test:on');
        assert.deepEqual({id: 42, login: 'hubot'}, event.detail);
        assert.strictEqual(document.body, event.target);
        assert.strictEqual(document.body, event.currentTarget);
        assert.strictEqual(document.body, this);
        assert.strictEqual(this, event.currentTarget);
        assert.instanceOf(event, CustomEvent);
      };
      on('test:on', 'body', observer);
      fire(document.body, 'test:on', {id: 42, login: 'hubot'});
      off('test:on', 'body', observer);
    });

    it('removes event observers', function() {
      const observer = (event) => assert.fail(event);
      on('test:off', '*', observer);
      off('test:off', '*', observer);
      fire(document.body, 'test:off');
    });

    it('can reregister after removing', function() {
      const [observer, trace] = spy((event) => assert.ok(event));
      on('test:register', 'body', observer);
      off('test:register', 'body', observer);
      on('test:register', 'body', observer);
      fire(document.body, 'test:register');
      off('test:register', 'body', observer);
      assert.equal(1, trace.calls);
    });
  });

  describe('event propagation', function() {
    before(function() {
      this.parent = document.querySelector('.js-test-parent');
      this.child = document.querySelector('.js-test-child');
    });

    it('fires observers in tree order', function() {
      const order = [];

      const parent = this.parent;
      const one = function(event) {
        assert.strictEqual(child, event.target);
        assert.strictEqual(parent, event.currentTarget);
        assert.strictEqual(this, event.currentTarget);
        assert.strictEqual(this, parent);
        order.push(1);
      };

      const child = this.child;
      const two = function(event) {
        assert.strictEqual(child, event.target);
        assert.strictEqual(child, event.currentTarget);
        assert.strictEqual(this, event.currentTarget);
        assert.strictEqual(this, child);
        order.push(2);
      };

      on('test:order', '.js-test-parent', one);
      on('test:order', '.js-test-child', two);
      fire(this.child, 'test:order');
      off('test:order', '.js-test-parent', one);
      off('test:order', '.js-test-child', two);

      assert.deepEqual([2, 1], order);
    });

    it('stops propagation bubbling to parent', function() {
      const one = (event) => assert.fail(event);
      const two = (event) => event.stopPropagation();
      on('test:bubble', '.js-test-parent', one);
      on('test:bubble', '.js-test-child', two);
      fire(this.child, 'test:bubble');
      off('test:bubble', '.js-test-parent', one);
      off('test:bubble', '.js-test-child', two);
    });

    it('stops immediate propagation', function() {
      const one = (event) => event.stopImmediatePropagation();
      const two = (event) => assert.fail(event);
      on('test:immediate', '.js-test-child', one);
      on('test:immediate', '.js-test-child', two);
      fire(this.child, 'test:immediate');
      off('test:immediate', '.js-test-child', one);
      off('test:immediate', '.js-test-child', two);
    });

    it('stops immediate propagation but not bubbling', function() {
      const [one, trace] = spy((event) => assert.ok(event));
      const two = (event) => event.stopImmediatePropagation();
      on('test:stop', '.js-test-parent', one);
      on('test:stop', '.js-test-child', two);
      fire(this.child, 'test:stop');
      assert.equal(1, trace.calls);
      off('test:stop', '.js-test-parent', one);
      off('test:stop', '.js-test-child', two);
    });

    it('calculates selector matches before dispatching event', function() {
      this.child.classList.add('inactive');

      const one = function(event) {
        event.target.classList.remove('inactive');
        event.target.classList.add('active');
        assert.ok(event);
      };

      const two = (event) => assert.fail(event);

      on('test:match', '.js-test-child.inactive', one);
      on('test:match', '.js-test-child.active', two);
      fire(this.child, 'test:match');
      off('test:match', '.js-test-child.inactive', one);
      off('test:match', '.js-test-child.active', two);

      this.child.classList.remove('active');
    });
  });

  function spy(fn) {
    const tracker = {calls: 0};
    const capture = function() {
      tracker.calls++;
      return fn.apply(this, arguments);
    };
    return [capture, tracker];
  }
});
<commit_msg>Add spy, fix equality order<commit_after>import {on, off, fire} from '../delegated-events';

describe('delegated event listeners', function() {
  describe('firing custom events', function() {
    it('fires custom events with detail', function() {
      const observer = function(event) {
        assert(event.bubbles);
        assert(event.cancelable);
        assert.equal(event.type, 'test:detail');
        assert.deepEqual(event.detail, {id: 42, login: 'hubot'});
        assert.strictEqual(document.body, event.target);
        assert.instanceOf(event, CustomEvent);
      };
      document.addEventListener('test:detail', observer);
      fire(document.body, 'test:detail', {id: 42, login: 'hubot'});
      document.removeEventListener('test:detail', observer);
    });

    it('fires custom events without detail', function() {
      const observer = function(event) {
        assert(event.detail === undefined || event.detail === null);
        assert.instanceOf(event, CustomEvent);
      };
      document.addEventListener('test:fire', observer);
      fire(document.body, 'test:fire');
      document.removeEventListener('test:fire', observer);
    });

    it('returns canceled when default prevented', function() {
      const observer = (event) => event.preventDefault();
      document.addEventListener('test:cancel', observer);
      const canceled = !fire(document.body, 'test:cancel');
      assert.equal(canceled, true);
      document.removeEventListener('test:cancel', observer);
    });

    it('returns not canceled when default is not prevented', function() {
      const [observer, trace] = spy((event) => assert.ok(event));
      document.addEventListener('test:event', observer);
      const canceled = !fire(document.body, 'test:event');
      assert.equal(canceled, false);
      assert.equal(trace.calls, 1);
      document.removeEventListener('test:event', observer);
    });
  });

  describe('registering event observers', function() {
    it('observes custom events', function() {
      const observer = function(event) {
        assert(event.bubbles);
        assert(event.cancelable);
        assert.equal(event.type, 'test:on');
        assert.deepEqual({id: 42, login: 'hubot'}, event.detail);
        assert.strictEqual(document.body, event.target);
        assert.strictEqual(document.body, event.currentTarget);
        assert.strictEqual(document.body, this);
        assert.strictEqual(this, event.currentTarget);
        assert.instanceOf(event, CustomEvent);
      };
      on('test:on', 'body', observer);
      fire(document.body, 'test:on', {id: 42, login: 'hubot'});
      off('test:on', 'body', observer);
    });

    it('removes event observers', function() {
      const observer = (event) => assert.fail(event);
      on('test:off', '*', observer);
      off('test:off', '*', observer);
      fire(document.body, 'test:off');
    });

    it('can reregister after removing', function() {
      const [observer, trace] = spy((event) => assert.ok(event));
      on('test:register', 'body', observer);
      off('test:register', 'body', observer);
      on('test:register', 'body', observer);
      fire(document.body, 'test:register');
      off('test:register', 'body', observer);
      assert.equal(trace.calls, 1);
    });
  });

  describe('event propagation', function() {
    before(function() {
      this.parent = document.querySelector('.js-test-parent');
      this.child = document.querySelector('.js-test-child');
    });

    it('fires observers in tree order', function() {
      const order = [];

      const parent = this.parent;
      const one = function(event) {
        assert.strictEqual(child, event.target);
        assert.strictEqual(parent, event.currentTarget);
        assert.strictEqual(this, event.currentTarget);
        assert.strictEqual(this, parent);
        order.push(1);
      };

      const child = this.child;
      const two = function(event) {
        assert.strictEqual(child, event.target);
        assert.strictEqual(child, event.currentTarget);
        assert.strictEqual(this, event.currentTarget);
        assert.strictEqual(this, child);
        order.push(2);
      };

      on('test:order', '.js-test-parent', one);
      on('test:order', '.js-test-child', two);
      fire(this.child, 'test:order');
      off('test:order', '.js-test-parent', one);
      off('test:order', '.js-test-child', two);

      assert.deepEqual([2, 1], order);
    });

    it('stops propagation bubbling to parent', function() {
      const one = (event) => assert.fail(event);
      const two = (event) => event.stopPropagation();
      on('test:bubble', '.js-test-parent', one);
      on('test:bubble', '.js-test-child', two);
      fire(this.child, 'test:bubble');
      off('test:bubble', '.js-test-parent', one);
      off('test:bubble', '.js-test-child', two);
    });

    it('stops immediate propagation', function() {
      const one = (event) => event.stopImmediatePropagation();
      const two = (event) => assert.fail(event);
      on('test:immediate', '.js-test-child', one);
      on('test:immediate', '.js-test-child', two);
      fire(this.child, 'test:immediate');
      off('test:immediate', '.js-test-child', one);
      off('test:immediate', '.js-test-child', two);
    });

    it('stops immediate propagation but not bubbling', function() {
      const [one, trace] = spy((event) => assert.ok(event));
      const two = (event) => event.stopImmediatePropagation();
      on('test:stop', '.js-test-parent', one);
      on('test:stop', '.js-test-child', two);
      fire(this.child, 'test:stop');
      assert.equal(trace.calls, 1);
      off('test:stop', '.js-test-parent', one);
      off('test:stop', '.js-test-child', two);
    });

    it('calculates selector matches before dispatching event', function() {
      this.child.classList.add('inactive');

      const one = function(event) {
        event.target.classList.remove('inactive');
        event.target.classList.add('active');
        assert.ok(event);
      };

      const two = (event) => assert.fail(event);

      on('test:match', '.js-test-child.inactive', one);
      on('test:match', '.js-test-child.active', two);
      fire(this.child, 'test:match');
      off('test:match', '.js-test-child.inactive', one);
      off('test:match', '.js-test-child.active', two);

      this.child.classList.remove('active');
    });
  });

  function spy(fn) {
    const tracker = {calls: 0};
    const capture = function() {
      tracker.calls++;
      return fn.apply(this, arguments);
    };
    return [capture, tracker];
  }
});
<|endoftext|>


<commit_before>import EventEmitter from 'event-emitter-es6'
import axios        from 'axios'

export default class SpeckleReceiver extends EventEmitter {
  constructor( args ) {
    super()

    let self = this

    this.serverUrl = args.serverUrl
    this.token = args.token
    this.streamId = args.streamId
    this.serverName = null
    this.wsEndpoint = null
    this.restEndpoint = null

    this.layers = []  // placeholders, they're filled in the get stream.
    this.objects = []  // placeholders, they're filled in the get stream.
    this.objectProperties = []  // placeholders, they're filled in the get stream.
    this.name = null // placeholders, they're filled in the get stream.
    this.history = [] // placeholders, they're filled in the get stream.
    this.layerMaterials = {}

    this.ws = null
    this.wsReconnectionAttempts = 0
    this.wsSessionId = null
    this.streamFound = false

    // list of server events mapped to functions
    this.spkEvents = {
      'ws-session-id' : self.setSessionId.bind( self ),
      'live-update': self.liveUpdate.bind( self ),
      'metadata-update': self.metadataUpdate.bind( self ),
      'history-update': self.historyUpdate.bind( self ),
      'volatile-broadcast': self.volatileBroadcast.bind( self ),
      'volatile-message': self.volatileMessage.bind( self ),
      'server-message': self.serverMessage.bind( self ),
      'error': self.propagateError.bind( self )
    }

    this.handshake( ( err, handshakeData ) => {
      if( err ) return this.emit( 'error', err )
      this.restEndpoint = handshakeData.restApi
      this.wsEndpoint = handshakeData.ws
      this.serverName = handshakeData.serverName

      // get the stream main info
      this.getStream()

      // handles ws disconnects
      this.wsConnectionCheker = setInterval( () => {
        if( ( !this.ws || this.ws.readyState == 3 ) && ( this.wsReconnectionAttempts < 20 ) ) {
          this.wsConnect()
          this.wsReconnectionAttempts++
        }
      }, 2000 )

      // emits the ready event if ws is connected and stream was found
      this.isReadyChecker = setInterval ( () => {
        if( !this.wsSessionId ) return
        if( !this.streamFound ) return

        console.log('SpeckeReceiver: Receiver ready ...')
        this.emit('ready', this.name, this.layers, this.objects, this.history, this.layerMaterials )
        clearInterval( this.isReadyChecker )
      }, 100 )
    })
  }

  handshake( callback ) {
    axios.get( this.serverUrl, { headers : { 'speckle-token': this.token } } )
    .then( response => {
      callback( null, response.data )
    } )
    .catch( error => {
      callback( error, null )
    })
  }

  wsConnect() {
    this.ws = new WebSocket( this.wsEndpoint + '/?access_token=' + this.token )

    this.ws.onopen = () => {
      this.wsReconnectionAttempts = 0
      this.ws.send( JSON.stringify( { eventName: "join-stream", args: { streamid: this.streamId, role: "receiver" } } ) )
    }

    this.ws.onmessage = msg => {
      if( msg.data === 'ping')
        return this.ws.send( 'alive' )

      let parsedMsg = JSON.parse( msg.data )
      if( this.spkEvents.hasOwnProperty( parsedMsg.eventName ) )
        this.spkEvents[parsedMsg.eventName] ( parsedMsg )
      else return this.emit( 'error', 'Undefined event received.' ) // console.log('Undefined event', parsedMsg.eventName )
    }

    this.ws.onclose = ( reason ) => {
      console.log( reason )
      console.log( 'Socket closed.' )
    }
  }

  getStream() {
    console.log('Attempting to retrieve stream.')
    let self = this
    axios.get( this.restEndpoint + '/streams/' + this.streamId + '/data/', { headers : { 'speckle-token': this.token,  'speckle-ws-id': this.wsSessionId } } )
    .then( response => {
      if( !response.data.success ) return this.emit( 'error', response.message )

      self.layers = response.data.data.layers
      self.objects = response.data.data.objects
      self.objectProperties = response.data.data.objectProperties

      self.name = response.data.data.name
      self.objectProperties.forEach( prop => {
        self.objects[ prop.objectIndex ].properties = prop.properties
      })
      return axios.get( this.restEndpoint + '/streams/' + this.streamId, { headers : { 'speckle-token': this.token,  'speckle-ws-id': this.wsSessionId } } )
    })
    .then( response => {
      if( !response.data.success ) return this.emit( 'error', response.message )

      self.history = response.data.data.history
      self.layerMaterials = response.data.data.layerMaterials
      self.streamFound = true
    })
    .catch( error => {
      this.emit( 'error', error )
    })
  }

  setSessionId ( msg ) {

    this.wsSessionId = msg.sessionId
  }

  /////////////////////////////////////////////////////////
  /// PUBLIC-esque methods
  /////////////////////////////////////////////////////////

  //gets all stream objects.
  getObjects( callback ) {
    let receivedObjects = []
    let objs = this.objects

    for(let i = 0; i< objs.length; i++)
      receivedObjects.push('placeholder')

    let extHead = 0
    objs.forEach( ( obj, index ) => {
      this.getObject( obj, response => {
        receivedObjects.splice( index, 1, response )
        this.emit( 'object-load-progress', objs.length, extHead )
        if( ++extHead >= objs.length ) return callback( receivedObjects )
      })
    })
  }

  getObject( obj, callback ) {
    console.log( obj )
    if( !obj ) {
      throw new Error('no obj provided')
      return
    }
    if( ! obj.hash )
      return callback( obj )

    axios.get( this.restEndpoint + '/geometry/' + obj.hash  )
      .then( response => {
        let myObject = response.data.data
        // reattach props
        myObject.properties = obj.properties
        return callback( myObject )
      } )
      .catch( err => {
        throw new Error( err )
      })
  }

  broadcast( message ) {
    if( !this.streamId )
      throw new Error( 'No streamId, where should I broadcast?' )
    this.ws.send( JSON.stringify( {
      eventName: "volatile-broadcast",
      args: JSON.stringify( message )
    } ) )
  }

  sendMessage( message, recipient ) {
    if( !this.streamId || !recipient )
      throw new Error( 'No streamId or recipient. Meeep!')
    this.ws.send( JSON.stringify( {
      eventName: 'volatile-message',
      args: { message: JSON.stringify( message ), recipient: recipient }
    } ))
  }

  /////////////////////////////////////////////////////////
  /// EVENTS
  /////////////////////////////////////////////////////////
  liveUpdate ( msg ) {
    this.name = msg.args.name
    this.layers = msg.args.layers
    this.objects = msg.args.objects
    this.objectProperties = msg.args.objectProperties

    this.objectProperties.forEach( prop => {
        this.objects[ prop.objectIndex ].properties = prop.properties
    })
    this.emit( 'live-update', msg.args.name, msg.args.layers, msg.args.objects, msg.args.objectProperties )
  }

  metadataUpdate ( msg ) {
    this.name = msg.args.name
    this.layers = msg.args.layers

    this.emit( 'metadata-update', msg.args.name, msg.args.layers )
  }

  historyUpdate ( msg ) {
    this.history = msg.args
    this.emit( 'history-update', msg.args )
  }

  volatileBroadcast ( msg ) {
    this.emit( 'volatile-broadcast', msg )
  }

  volatileMessage ( msg ) {
    console.log( '!!! Got a volatile message')
    this.emit( 'volatile-message', msg )
  }

  serverMessage( msg ) {
    this.emit( 'server-message', msg )
  }

  propagateError( msg ) {
    this.emit( 'error', msg )
  }

  dispose() {
    this.ws.close()
    this.ws = null
    clearInterval( this.isReadyChecker )
    clearInterval( this.wsConnectionCheker )
  }
}<commit_msg>minor bugfix in the receiver<commit_after>import EventEmitter from 'event-emitter-es6'
import axios        from 'axios'

export default class SpeckleReceiver extends EventEmitter {
  constructor( args ) {
    super()

    let self = this

    this.serverUrl = args.serverUrl
    this.token = args.token
    this.streamId = args.streamId
    this.serverName = null
    this.wsEndpoint = null
    this.restEndpoint = null

    this.layers = []  // placeholders, they're filled in the get stream.
    this.objects = []  // placeholders, they're filled in the get stream.
    this.objectProperties = []  // placeholders, they're filled in the get stream.
    this.name = null // placeholders, they're filled in the get stream.
    this.history = [] // placeholders, they're filled in the get stream.
    this.layerMaterials = {}

    this.ws = null
    this.wsReconnectionAttempts = 0
    this.wsSessionId = null
    this.streamFound = false

    // list of server events mapped to functions
    this.spkEvents = {
      'ws-session-id' : self.setSessionId.bind( self ),
      'live-update': self.liveUpdate.bind( self ),
      'metadata-update': self.metadataUpdate.bind( self ),
      'history-update': self.historyUpdate.bind( self ),
      'volatile-broadcast': self.volatileBroadcast.bind( self ),
      'volatile-message': self.volatileMessage.bind( self ),
      'server-message': self.serverMessage.bind( self ),
      'error': self.propagateError.bind( self )
    }

    this.handshake( ( err, handshakeData ) => {
      if( err ) return this.emit( 'error', err )
      this.restEndpoint = handshakeData.restApi
      this.wsEndpoint = handshakeData.ws
      this.serverName = handshakeData.serverName

      // get the stream main info
      this.getStream()

      // handles ws disconnects
      this.wsConnectionCheker = setInterval( () => {
        if( ( !this.ws || this.ws.readyState == 3 ) && ( this.wsReconnectionAttempts < 20 ) ) {
          this.wsConnect()
          this.wsReconnectionAttempts++
        }
      }, 2000 )

      // emits the ready event if ws is connected and stream was found
      this.isReadyChecker = setInterval ( () => {
        if( !this.wsSessionId ) return
        if( !this.streamFound ) return

        console.log('SpeckeReceiver: Receiver ready ...')
        this.emit('ready', this.name, this.layers, this.objects, this.history, this.layerMaterials )
        clearInterval( this.isReadyChecker )
      }, 100 )
    })
  }

  handshake( callback ) {
    axios.get( this.serverUrl, { headers : { 'speckle-token': this.token } } )
    .then( response => {
      callback( null, response.data )
    } )
    .catch( error => {
      callback( error, null )
    })
  }

  wsConnect() {
    this.ws = new WebSocket( this.wsEndpoint + '/?access_token=' + this.token )

    this.ws.onopen = () => {
      this.wsReconnectionAttempts = 0
      this.ws.send( JSON.stringify( { eventName: "join-stream", args: { streamid: this.streamId, role: "receiver" } } ) )
    }

    this.ws.onmessage = msg => {
      if( msg.data === 'ping')
        return this.ws.send( 'alive' )

      let parsedMsg = JSON.parse( msg.data )
      if( this.spkEvents.hasOwnProperty( parsedMsg.eventName ) )
        this.spkEvents[parsedMsg.eventName] ( parsedMsg )
      else return this.emit( 'error', 'Undefined event received.' ) // console.log('Undefined event', parsedMsg.eventName )
    }

    this.ws.onclose = ( reason ) => {
      console.log( reason )
      console.log( 'Socket closed.' )
    }
  }

  getStream() {
    console.log('Attempting to retrieve stream.')
    let self = this
    axios.get( this.restEndpoint + '/streams/' + this.streamId + '/data/', { headers : { 'speckle-token': this.token,  'speckle-ws-id': this.wsSessionId } } )
    .then( response => {
      if( !response.data.success ) return this.emit( 'error', response.message )

      self.layers = response.data.data.layers
      self.objects = response.data.data.objects
      self.objectProperties = response.data.data.objectProperties

      self.name = response.data.data.name
      self.objectProperties.forEach( prop => {
        self.objects[ prop.objectIndex ].properties = prop.properties
      })
      return axios.get( this.restEndpoint + '/streams/' + this.streamId, { headers : { 'speckle-token': this.token,  'speckle-ws-id': this.wsSessionId } } )
    })
    .then( response => {
      if( !response.data.success ) return this.emit( 'error', response.message )

      self.history = response.data.data.history
      self.layerMaterials = response.data.data.layerMaterials
      self.streamFound = true
    })
    .catch( error => {
      this.emit( 'error', error )
    })
  }

  setSessionId ( msg ) {

    this.wsSessionId = msg.sessionId
  }

  /////////////////////////////////////////////////////////
  /// PUBLIC-esque methods
  /////////////////////////////////////////////////////////

  //gets all stream objects.
  getObjects( callback ) {
    let receivedObjects = []
    let objs = this.objects

    for(let i = 0; i< objs.length; i++)
      receivedObjects.push('placeholder')

    let extHead = 0
    objs.forEach( ( obj, index ) => {
      this.getObject( obj, response => {
        receivedObjects.splice( index, 1, response )
        this.emit( 'object-load-progress', objs.length, extHead )
        if( ++extHead >= objs.length ) return callback( receivedObjects )
      })
    })
  }

  getObject( obj, callback ) {
    console.log( obj )
    if( !obj ) {
      throw new Error('no obj provided')
      return
    }
    if( obj.type === 'Curve' || obj.type ==='Polyline' || obj.type === 'Mesh' || obj.type === 'Brep' )
      axios.get( this.restEndpoint + '/geometry/' + obj.hash  )
        .then( response => {
          let myObject = response.data.data
          // reattach props
          myObject.properties = obj.properties
          return callback( myObject )
        } )
        .catch( err => {
          throw new Error( err )
        })
    else
      return callback( obj )
  }

  broadcast( message ) {
    if( !this.streamId )
      throw new Error( 'No streamId, where should I broadcast?' )
    this.ws.send( JSON.stringify( {
      eventName: "volatile-broadcast",
      args: JSON.stringify( message )
    } ) )
  }

  sendMessage( message, recipient ) {
    if( !this.streamId || !recipient )
      throw new Error( 'No streamId or recipient. Meeep!')
    this.ws.send( JSON.stringify( {
      eventName: 'volatile-message',
      args: { message: JSON.stringify( message ), recipient: recipient }
    } ))
  }

  /////////////////////////////////////////////////////////
  /// EVENTS
  /////////////////////////////////////////////////////////
  liveUpdate ( msg ) {
    this.name = msg.args.name
    this.layers = msg.args.layers
    this.objects = msg.args.objects
    this.objectProperties = msg.args.objectProperties

    this.objectProperties.forEach( prop => {
        this.objects[ prop.objectIndex ].properties = prop.properties
    })
    this.emit( 'live-update', msg.args.name, msg.args.layers, msg.args.objects, msg.args.objectProperties )
  }

  metadataUpdate ( msg ) {
    this.name = msg.args.name
    this.layers = msg.args.layers

    this.emit( 'metadata-update', msg.args.name, msg.args.layers )
  }

  historyUpdate ( msg ) {
    this.history = msg.args
    this.emit( 'history-update', msg.args )
  }

  volatileBroadcast ( msg ) {
    this.emit( 'volatile-broadcast', msg )
  }

  volatileMessage ( msg ) {
    console.log( '!!! Got a volatile message')
    this.emit( 'volatile-message', msg )
  }

  serverMessage( msg ) {
    this.emit( 'server-message', msg )
  }

  propagateError( msg ) {
    this.emit( 'error', msg )
  }

  dispose() {
    this.ws.close()
    this.ws = null
    clearInterval( this.isReadyChecker )
    clearInterval( this.wsConnectionCheker )
  }
}<|endoftext|>


<commit_before>/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright © 2017 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms
 * of the GNU Affero General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program. If not, see
 * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
 */

(function() {

    'use strict';

    /**
     * @ngdoc service
     * @name stock-physical-inventory-draft.physicalInventoryDraftService
     *
     * @description
     * Responsible for searching by keyword.
     */
    angular
        .module('stock-physical-inventory-draft')
        .service('physicalInventoryDraftService', service);

    service.$inject = ['$resource', 'stockmanagementUrlFactory', 'messageService', 'openlmisDateFilter', 'productNameFilter'];

    function service($resource, stockmanagementUrlFactory, messageService, openlmisDateFilter, productNameFilter) {
        var resource = $resource(stockmanagementUrlFactory('/api/physicalInventories/draft'), {}, {
            submitPhysicalInventory: {
                method: 'POST',
                url: stockmanagementUrlFactory('/api/stockEvents')
            }
        });

        this.search = search;
        this.saveDraft = saveDraft;
        this.delete = deleteDraft;
        this.submitPhysicalInventory = submit;

        /**
         * @ngdoc method
         * @methodOf stock-physical-inventory-draft.physicalInventoryDraftService
         * @name search
         *
         * @description
         * Searching from given line items by keyword.
         *
         * @param {String} keyword   keyword
         * @param {Array}  lineItems all line items
         * @return {Array} result    search result
         */
        function search(keyword, lineItems) {
            var result = lineItems;
            var hasLot = _.any(lineItems, function(item) {
                return item.lot;
            });

            if (!_.isEmpty(keyword)) {
                keyword = keyword.trim();
                result = _.filter(lineItems, function (item) {
                    var hasStockOnHand = !(_.isNull(item.stockOnHand) || _.isUndefined(item.stockOnHand));
                    var hasQuantity = !(_.isNull(item.quantity) || _.isUndefined(item.quantity)) && item.quantity !== -1;
                    var searchableFields = [
                        item.orderable.productCode, productNameFilter(item.orderable),
                        hasStockOnHand ? item.stockOnHand.toString() : "",
                        hasQuantity ? item.quantity.toString() : "",
                        item.lot ? item.lot.lotCode : (hasLot? messageService.get('orderableLotUtilService.noLotDefined') : ""),
                        item.lot ? openlmisDateFilter(item.lot.expirationDate) : ""
                    ];
                    return _.any(searchableFields, function (field) {
                        return field.toLowerCase().contains(keyword.toLowerCase());
                    });
                });
            }

            return result;
        }

        /**
         * @ngdoc method
         * @methodOf stock-physical-inventory-draft.physicalInventoryDraftService
         * @name saveDraft
         *
         * @description
         * Saves physical inventory draft.
         *
         * @param  {Object} draft Draft that will be saved
         * @return {Promise}      Saved draft
         */
        function saveDraft(draft) {
            return resource.save(draft).$promise;
        }

        function deleteDraft(programId, facilityId) {
            return resource.delete({program: programId, facility: facilityId}).$promise;
        }

        /**
         * @ngdoc method
         * @methodOf stock-physical-inventory-draft.physicalInventoryDraftService
         * @name submit
         *
         * @description
         * Submits physical inventory draft.
         *
         * @param  {Object} draft Draft that will be saved
         * @return {Promise}      Submitted Physical Inventory
         */
        function submit(physicalInventory) {
            var event = _.clone(physicalInventory);
            event.lineItems = physicalInventory.lineItems
            .filter(function (item) {
                return item.isAdded;
            })
            .map(function (item) {
                return {
                    orderableId: item.orderable.id,
                    lotId: item.lot ? item.lot.id : null,
                    quantity: item.quantity,
                    occurredDate: physicalInventory.occurredDate,
                    extraData: {
                        vvmStatus: item.vvmStatus
                    }
                };
            });

            return resource.submitPhysicalInventory(event).$promise;
        }
    }
})();
<commit_msg>OLMIS-2711: The list of stock adjustments will now be sent along with the line item when submitting draft<commit_after>/*
 * This program is part of the OpenLMIS logistics management information system platform software.
 * Copyright © 2017 VillageReach
 *
 * This program is free software: you can redistribute it and/or modify it under the terms
 * of the GNU Affero General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU Affero General Public License for more details. You should have received a copy of
 * the GNU Affero General Public License along with this program. If not, see
 * http://www.gnu.org/licenses.  For additional information contact info@OpenLMIS.org.
 */

(function() {

    'use strict';

    /**
     * @ngdoc service
     * @name stock-physical-inventory-draft.physicalInventoryDraftService
     *
     * @description
     * Responsible for searching by keyword.
     */
    angular
        .module('stock-physical-inventory-draft')
        .service('physicalInventoryDraftService', service);

    service.$inject = ['$resource', 'stockmanagementUrlFactory', 'messageService', 'openlmisDateFilter', 'productNameFilter'];

    function service($resource, stockmanagementUrlFactory, messageService, openlmisDateFilter, productNameFilter) {
        var resource = $resource(stockmanagementUrlFactory('/api/physicalInventories/draft'), {}, {
            submitPhysicalInventory: {
                method: 'POST',
                url: stockmanagementUrlFactory('/api/stockEvents')
            }
        });

        this.search = search;
        this.saveDraft = saveDraft;
        this.delete = deleteDraft;
        this.submitPhysicalInventory = submit;

        /**
         * @ngdoc method
         * @methodOf stock-physical-inventory-draft.physicalInventoryDraftService
         * @name search
         *
         * @description
         * Searching from given line items by keyword.
         *
         * @param {String} keyword   keyword
         * @param {Array}  lineItems all line items
         * @return {Array} result    search result
         */
        function search(keyword, lineItems) {
            var result = lineItems;
            var hasLot = _.any(lineItems, function(item) {
                return item.lot;
            });

            if (!_.isEmpty(keyword)) {
                keyword = keyword.trim();
                result = _.filter(lineItems, function (item) {
                    var hasStockOnHand = !(_.isNull(item.stockOnHand) || _.isUndefined(item.stockOnHand));
                    var hasQuantity = !(_.isNull(item.quantity) || _.isUndefined(item.quantity)) && item.quantity !== -1;
                    var searchableFields = [
                        item.orderable.productCode, productNameFilter(item.orderable),
                        hasStockOnHand ? item.stockOnHand.toString() : "",
                        hasQuantity ? item.quantity.toString() : "",
                        item.lot ? item.lot.lotCode : (hasLot? messageService.get('orderableLotUtilService.noLotDefined') : ""),
                        item.lot ? openlmisDateFilter(item.lot.expirationDate) : ""
                    ];
                    return _.any(searchableFields, function (field) {
                        return field.toLowerCase().contains(keyword.toLowerCase());
                    });
                });
            }

            return result;
        }

        /**
         * @ngdoc method
         * @methodOf stock-physical-inventory-draft.physicalInventoryDraftService
         * @name saveDraft
         *
         * @description
         * Saves physical inventory draft.
         *
         * @param  {Object} draft Draft that will be saved
         * @return {Promise}      Saved draft
         */
        function saveDraft(draft) {
            return resource.save(draft).$promise;
        }

        function deleteDraft(programId, facilityId) {
            return resource.delete({program: programId, facility: facilityId}).$promise;
        }

        /**
         * @ngdoc method
         * @methodOf stock-physical-inventory-draft.physicalInventoryDraftService
         * @name submit
         *
         * @description
         * Submits physical inventory draft.
         *
         * @param  {Object} draft Draft that will be saved
         * @return {Promise}      Submitted Physical Inventory
         */
        function submit(physicalInventory) {
            var event = _.clone(physicalInventory);
            event.lineItems = physicalInventory.lineItems
            .filter(function (item) {
                return item.isAdded;
            })
            .map(function (item) {
                return {
                    orderableId: item.orderable.id,
                    lotId: item.lot ? item.lot.id : null,
                    quantity: item.quantity,
                    occurredDate: physicalInventory.occurredDate,
                    extraData: {
                        vvmStatus: item.vvmStatus
                    },
                    stockAdjustments: item.stockAdjustments
                };
            });

            return resource.submitPhysicalInventory(event).$promise;
        }
    }
})();
<|endoftext|>