text: 'use strict';
  const testHelper = require('../../../test_helper');
  const expect = testHelper.expect;
  const sinon = testHelper.sinon;
  const MessageProcessor = testHelper.requireSourceModule(module);
  
  const Config = testHelper.requireSourceModule(module, 'lib/config');
  const HookBase = testHelper.requireSourceModule(module, 'lib/hook/Base');
  const HookMessage = testHelper.requireSourceModule(module, 'lib/hook/Message');
  const HookContextBase = testHelper.requireSourceModule(module, 'lib/hook-context/base');
  
  // Shorthand to make writing these easier
  const EMH = MessageProcessor.ERRORS_MODIFIED_HEADER + '\n';
  const WMH = MessageProcessor.WARNINGS_MODIFIED_HEADER + '\n';
  const EUH = MessageProcessor.ERRORS_UNMODIFIED_HEADER + '\n';
  const WUH = MessageProcessor.WARNINGS_UNMODIFIED_HEADER + '\n';
  const EGH = MessageProcessor.ERRORS_GENERIC_HEADER + '\n';
  const WGH = MessageProcessor.WARNINGS_GENERIC_HEADER + '\n';
  
  function errorMess(fileName, lineNumber) {
    return new HookMessage('error', fileName, lineNumber, 'Error!');
  }
  
  function warnMess(fileName, lineNumber) {
    return new HookMessage('warning', fileName, lineNumber, 'Warning!');
  }
  
  describe('MessageProcessor', function () {
    beforeEach('create message processor instance', function() {
      this.sandbox = sinon.sandbox.create();
  
      this.config = new Config({}, {validate: false});
      this.sandbox.stub(this.config, 'forHook').returns({});
  
      this.context = new HookContextBase(this.config, [], process.stdin);
      this.hook = new HookBase(this.config, this.context);
  
      this.unmodifiedLinesSetting = 'report';
      this.messageProcessor = new MessageProcessor(this.hook, this.unmodifiedLinesSetting);
    });
  
    afterEach('restore sandbox', function() {
      this.sandbox.restore();
    });
  
    describe('#hookResult', function() {
      describe('when there are no messages', function() {
        it('returns a pass when unmodifiedLinesSetting is report', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'report';
          let hookResult = this.messageProcessor.hookResult([]);
          expect(hookResult).to.have.property('status', 'pass');
          expect(hookResult).to.have.property('output', '');
        });
  
        it('returns a pass when unmodifiedLinesSetting is warn', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'warn';
          let hookResult = this.messageProcessor.hookResult([]);
          expect(hookResult).to.have.property('status', 'pass');
          expect(hookResult).to.have.property('output', '');
        });
  
        it('returns a pass when unmodifiedLinesSetting is ignore', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'ignore';
          let hookResult = this.messageProcessor.hookResult([]);
          expect(hookResult).to.have.property('status', 'pass');
          expect(hookResult).to.have.property('output', '');
        });
      });
  
      describe('when there is an error on modified lines', function() {
        beforeEach('setup messages', function() {
          this.messages = [errorMess('node.js', 2)];
          this.context.modifiedLinesInFile = this.sandbox.stub();
          this.context.modifiedLinesInFile.withArgs('node.js').returns(['2']);
        });
  
        it('returns an error when unmodifiedLinesSetting is report', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'report';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', EMH + 'Error!\n');
        });
  
        it('returns an error when unmodifiedLinesSetting is warn', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'warn';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', EMH + 'Error!\n');
        });
  
        it('returns an error when unmodifiedLinesSetting is ignore', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'ignore';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', EMH + 'Error!\n');
        });
  
        it('passes if the ignoreMessagePattern filters out all messages', function() {
          this.messageProcessor.ignoreMessagePattern = RegExp('Error!');
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'pass');
        });
      });
  
      describe('when there are different errors on modified lines', function() {
        beforeEach('setup messages', function () {
          this.messages = [
            new HookMessage('error', 'node.js', 2, 'Error 1!'),
            new HookMessage('error', 'node.js', 3, 'Error 2!'),
          ];
          this.context.modifiedLinesInFile = this.sandbox.stub();
          this.context.modifiedLinesInFile.withArgs('node.js').returns(['2', '3']);
        });
  
        it('returns an error if ignoreMessagePattern matches a subset of the errors', function() {
          this.messageProcessor.ignoreMessagePattern = RegExp('Error 1!');
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', EMH + 'Error 2!\n');
        });
  
        it('passes if ignoreMessagePattern matches all of the errors', function() {
          this.messageProcessor.ignoreMessagePattern = RegExp('Error.*!');
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'pass');
        });
      });
  
      describe('when there is an error on unmodified lines', function() {
        beforeEach('setup messages', function() {
          this.messages = [errorMess('node.js', 2)];
          this.context.modifiedLinesInFile = this.sandbox.stub();
          this.context.modifiedLinesInFile.withArgs('node.js').returns(['3']);
        });
  
        it('returns an error when unmodifiedLinesSetting is report', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'report';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', EUH + 'Error!\n');
        });
  
        it('returns an error when unmodifiedLinesSetting is warn', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'warn';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'warn');
          expect(hookResult).to.have.property('output', EUH + 'Error!\n');
        });
  
        it('passes when unmodifiedLinesSetting is ignore', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'ignore';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'pass');
          expect(hookResult).to.have.property('output', '');
        });
      });
  
      describe('when there is a warning on modified lines', function() {
        beforeEach('setup messages', function() {
          this.messages = [warnMess('node.js', 2)];
          this.context.modifiedLinesInFile = this.sandbox.stub();
          this.context.modifiedLinesInFile.withArgs('node.js').returns(['2']);
        });
  
        it('returns an error when unmodifiedLinesSetting is report', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'report';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'warn');
          expect(hookResult).to.have.property('output', WMH + 'Warning!\n');
        });
  
        it('returns an error when unmodifiedLinesSetting is warn', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'warn';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'warn');
          expect(hookResult).to.have.property('output', WMH + 'Warning!\n');
        });
  
        it('returns a warning when unmodifiedLinesSetting is ignore', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'ignore';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'warn');
          expect(hookResult).to.have.property('output', WMH + 'Warning!\n');
        });
  
        it('passes if the ignoreMessagePattern filters out all warnings', function() {
          this.messageProcessor.ignoreMessagePattern = RegExp('Warning!');
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'pass');
        });
      });
  
      describe('when there is a warning on unmodified lines', function() {
        beforeEach('setup messages', function() {
          this.messages = [warnMess('node.js', 2)];
          this.context.modifiedLinesInFile = this.sandbox.stub();
          this.context.modifiedLinesInFile.withArgs('node.js').returns(['3']);
        });
  
        it('returns an error when unmodifiedLinesSetting is report', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'report';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'warn');
          expect(hookResult).to.have.property('output', WUH + 'Warning!\n');
        });
  
        it('returns an error when unmodifiedLinesSetting is warn', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'warn';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'warn');
          expect(hookResult).to.have.property('output', WUH + 'Warning!\n');
        });
  
        it('passes when unmodifiedLinesSetting is ignore', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'ignore';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'pass');
          expect(hookResult).to.have.property('output', '');
        });
      });
  
      describe('when there are errors and warnings on modified lines', function() {
        beforeEach('setup messages', function() {
          this.messages = [errorMess('affiance.js', 2), warnMess('node.js', 3)];
          this.context.modifiedLinesInFile = this.sandbox.stub();
          this.context.modifiedLinesInFile.withArgs('affiance.js').returns(['2']);
          this.context.modifiedLinesInFile.withArgs('node.js').returns(['3', '4']);
        });
  
        it('returns an error when unmodifiedLinesSetting is report', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'report';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', EMH + 'Error!\n' + WMH + 'Warning!\n');
        });
  
        it('returns an error when unmodifiedLinesSetting is warn', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'warn';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', EMH + 'Error!\n' + WMH + 'Warning!\n');
        });
  
        it('returns an error when unmodifiedLinesSetting is ignore', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'ignore';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', EMH + 'Error!\n' + WMH + 'Warning!\n');
        });
  
        it('warns if the ignoreMessagePattern filters out all errors', function() {
          this.messageProcessor.ignoreMessagePattern = RegExp('Error!');
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'warn');
        });
  
        it('passes if the ignoreMessagePattern filters out all errors and warnings', function() {
          this.messageProcessor.ignoreMessagePattern = RegExp('(Error|Warning)');
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'pass');
        });
      });
  
      describe('when there are errors and warnings on unmodified lines', function() {
        beforeEach('setup messages', function() {
          this.messages = [errorMess('affiance.js', 2), warnMess('node.js', 3)];
          this.context.modifiedLinesInFile = this.sandbox.stub();
          this.context.modifiedLinesInFile.withArgs('affiance.js').returns(['3']);
          this.context.modifiedLinesInFile.withArgs('node.js').returns(['4', '5']);
        });
  
        it('returns an error when unmodifiedLinesSetting is report', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'report';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', EUH + 'Error!\n' + WUH + 'Warning!\n');
        });
  
        it('returns an error when unmodifiedLinesSetting is warn', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'warn';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'warn');
          expect(hookResult).to.have.property('output', EUH + 'Error!\n' + WUH + 'Warning!\n');
        });
  
        it('returns an error when unmodifiedLinesSetting is ignore', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'ignore';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'pass');
          expect(hookResult).to.have.property('output', '');
        });
  
        it('warns if the ignoreMessagePattern filters out all errors and unmodifiedLinesSetting is report', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'report';
          this.messageProcessor.ignoreMessagePattern = RegExp('Error!');
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'warn');
        });
  
        it('passes if the ignoreMessagePattern filters out all errors and warnings and unmodifiedLinesSetting is report', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'report';
          this.messageProcessor.ignoreMessagePattern = RegExp('(Error|Warning)');
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'pass');
        });
  
      });
  
      describe('when there are errors and warnings on unmodified/modified lines', function() {
        beforeEach('setup messages', function() {
          this.messages = [
            errorMess('affiance.js', 2),
            warnMess('affiance.js', 3),
            warnMess('node.js', 4),
            errorMess('node.js', 5)
          ];
          this.context.modifiedLinesInFile = this.sandbox.stub();
          this.context.modifiedLinesInFile.withArgs('affiance.js').returns(['3', '4']);
          this.context.modifiedLinesInFile.withArgs('node.js').returns(['5', '6']);
        });
  
        it('returns an error when unmodifiedLinesSetting is report', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'report';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property(
            'output',
            EMH + 'Error!\n' + WMH + 'Warning!\n' +
            EUH + 'Error!\n' + WUH + 'Warning!\n'
          );
        });
  
        it('returns an error when unmodifiedLinesSetting is warn', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'warn';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property(
            'output',
            EMH + 'Error!\n' + WMH + 'Warning!\n' + EUH + 'Error!\n' + WUH + 'Warning!\n'
          );
        });
  
        it('returns an error when unmodifiedLinesSetting is ignore', function() {
          this.messageProcessor.unmodifiedLinesSetting = 'ignore';
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', EMH + 'Error!\n' + WMH + 'Warning!\n');
        });
      });
  
      describe('when there are generic errors', function() {
        beforeEach('setup messages', function() {
          this.messages = [errorMess(), errorMess()];
          this.messageProcessor.unmodifiedLinesSetting = 'report';
        });
  
        it('returns the errors', function() {
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', 'Error!\nError!\n');
        });
  
        it('passes if the ignoreMessagePattern filters out all errors', function() {
          this.messageProcessor.ignoreMessagePattern = RegExp('Error!');
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'pass');
        });
      });
  
      describe('when there are generic warnings', function() {
        beforeEach('setup messages', function() {
          this.messages = [warnMess(), warnMess()];
          this.messageProcessor.unmodifiedLinesSetting = 'report';
        });
  
        it('returns the warnings', function() {
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'warn');
          expect(hookResult).to.have.property('output', 'Warning!\nWarning!\n');
        });
      });
  
      describe('when there are generic errors and warnings', function() {
        beforeEach('setup messages', function() {
          this.messages = [warnMess(), errorMess(), warnMess(), errorMess()];
          this.messageProcessor.unmodifiedLinesSetting = 'report';
        });
  
        it('returns the warnings', function() {
          let hookResult = this.messageProcessor.hookResult(this.messages);
          expect(hookResult).to.have.property('status', 'fail');
          expect(hookResult).to.have.property('output', 'Warning!\nError!\nWarning!\nError!\n');
        });
      });
  
      describe('when there are mix of messages', function() {
        beforeEach('setup line messages and modified lines', function() {
          this.lineMessages = [
            errorMess('affiance.js', 2),
            warnMess('affiance.js', 3),
            warnMess('node.js', 4),
            errorMess('node.js', 5)
          ];
          this.context.modifiedLinesInFile = this.sandbox.stub();
          this.context.modifiedLinesInFile.withArgs('affiance.js').returns(['3', '4']);
          this.context.modifiedLinesInFile.withArgs('node.js').returns(['5', '6']);
        });
  
        describe('and there are generic errors before line messages', function() {
          beforeEach('setup messages', function() {
            this.messages = [errorMess(), errorMess()].concat(this.lineMessages);
          });
  
          it('returns combined failure output', function() {
            let hookResult = this.messageProcessor.hookResult(this.messages);
            expect(hookResult).to.have.property('status', 'fail');
            expect(hookResult).to.have.property(
              'output',
              EGH + 'Error!\nError!\n' +
              EMH + 'Error!\n' + WMH + 'Warning!\n' +
              EUH + 'Error!\n' + WUH + 'Warning!\n'
            );
          });
        });
  
        describe('and there are generic warnings before line messages', function() {
          beforeEach('setup messages', function() {
            this.messages = [warnMess(), warnMess()].concat(this.lineMessages);
          });
  
          it('returns combined failure output', function() {
            let hookResult = this.messageProcessor.hookResult(this.messages);
            expect(hookResult).to.have.property('status', 'fail');
            expect(hookResult).to.have.property(
              'output',
              WGH + 'Warning!\nWarning!\n' +
              EMH + 'Error!\n' + WMH + 'Warning!\n' +
              EUH + 'Error!\n' + WUH + 'Warning!\n'
            );
          });
        });
  
        describe('and there are generic errors and warnings before line messages', function() {
          beforeEach('setup messages', function() {
            this.messages = [errorMess(), errorMess(), warnMess(), warnMess()].concat(this.lineMessages);
          });
  
          it('returns combined failure output', function() {
            let hookResult = this.messageProcessor.hookResult(this.messages);
            expect(hookResult).to.have.property('status', 'fail');
            expect(hookResult).to.have.property(
              'output',
              EGH + 'Error!\nError!\n' +
              WGH + 'Warning!\nWarning!\n' +
              EMH + 'Error!\n' + WMH + 'Warning!\n' +
              EUH + 'Error!\n' + WUH + 'Warning!\n'
            );
          });
        });
  
        describe('and there are generic errors after line messages', function() {
          beforeEach('setup messages', function() {
            this.messages = this.lineMessages.concat([errorMess(), errorMess()]);
          });
  
          it('returns combined failure output', function() {
            let hookResult = this.messageProcessor.hookResult(this.messages);
            expect(hookResult).to.have.property('status', 'fail');
            expect(hookResult).to.have.property(
              'output',
              EGH + 'Error!\nError!\n' +
              EMH + 'Error!\n' + WMH + 'Warning!\n' +
              EUH + 'Error!\n' + WUH + 'Warning!\n'
            );
          });
        });
  
        describe('and there are generic warnings after line messages', function() {
          beforeEach('setup messages', function() {
            this.messages = this.lineMessages.concat([warnMess(), warnMess()]);
          });
  
          it('returns combined failure output', function() {
            let hookResult = this.messageProcessor.hookResult(this.messages);
            expect(hookResult).to.have.property('status', 'fail');
            expect(hookResult).to.have.property(
              'output',
              WGH + 'Warning!\nWarning!\n' +
              EMH + 'Error!\n' + WMH + 'Warning!\n' +
              EUH + 'Error!\n' + WUH + 'Warning!\n'
            );
          });
        });
      });
    });
  });
  