Home Manual Reference Source Test

test/lib/util/ExternalCommand.spec.js

import Emitter from 'events';
import { Buffer } from 'buffer';
import { stub, spy } from 'sinon';
import proxyquire from 'proxyquire';
import expect from 'unexpected';

const which = stub().callsArgWith(1, null, 'bin');
const stubProcess = Object.assign(new Emitter(), {
  stdout: new Emitter(),
  stderr: new Emitter(),
});
const spawn = stub().returns(stubProcess);

const External = proxyquire('../../../src/lib/util/ExternalCommand', {
  which,
  child_process: {
    spawn,
  },
}).default;

/** @test {ExternalCommand} */
describe('External', function () {
  beforeEach(function () {
    which.resetHistory();
    which.callsArgWith(1, null, 'bin');
  });

  /** @test {ExternalCommand#resolveBin} */
  describe('#resolveBin', function () {
    it('should be fulfilled with which results', function () {
      which.callsArgWith(1, null, 'bin');

      return expect(External.resolveBin('asdf'), 'to be fulfilled with', 'bin').then(() => {
        expect(which.calledOnce, 'to be', true);
        expect(which.lastCall.args[0], 'to equal', 'asdf');
      });
    });

    it('should reject on which error', function () {
      which.callsArgWith(1, new Error('which error'));

      return expect(External.resolveBin('asdf'), 'to be rejected with', 'which error').then(() => {
        expect(which.calledOnce, 'to be', true);
        expect(which.lastCall.args[0], 'to equal', 'asdf');
      });
    });
  });

  /** @test {ExternalCommand#spawn} */
  describe('#spawn', function () {
    it('should be rejected on spawn error', function () {
      const p = expect(External.spawn('asdf'), 'to be rejected with', 'spawn error');

      stubProcess.emit('error', new Error('spawn error'));

      return p;
    });

    it('should be rejected on non-zero exit code', function () {
      const p = expect(External.spawn('asdf'), 'to be rejected with', /returned code 123$/);

      stubProcess.emit('close', 123);

      return p;
    });

    it('should be fulfill with trimmed stdout data', function () {
      const p = expect(External.spawn('asdf'), 'to be fulfilled with', 'data');

      stubProcess.stdout.emit('data', Buffer.from('data\n'));
      stubProcess.emit('close', 0);

      return p;
    });

    it('should pass args to spawn', function () {
      const args = ['--test'];
      const p = expect(External.spawn('asdf', args), 'to be fulfilled').then(() =>
        expect(spawn.calledWith('asdf', args), 'to be true')
      );

      stubProcess.emit('close', 0);

      return p;
    });

    it('should pass options.spawn to spawn', function () {
      const spawnOpts = { opt: 13 };
      const p = expect(
        External.spawn('asdf', [], { spawn: spawnOpts }),
        'to be fulfilled'
      ).then(() => expect(spawn.calledWith('asdf', [], spawnOpts), 'to be true'));

      stubProcess.emit('close', 0);

      return p;
    });

    it('should call options.onspawn after spawn', function () {
      const onspawn = spy();
      const p = expect(External.spawn('asdf', [], { onspawn }), 'to be fulfilled')
        .then(() => expect(onspawn.calledOnce, 'to be true'))
        .then(() => expect(onspawn.lastCall.args[0], 'to be', stubProcess));

      stubProcess.emit('close', 0);

      return p;
    });
  });

  /** @test {ExternalCommand#run} */
  describe('#run', function () {
    it('should call #resolveBin with name', function () {
      return expect(
        External.run('asdf', [], {
          onspawn() {
            stubProcess.emit('close', 0);
          },
        }),
        'to be fulfilled'
      )
        .then(() => expect(which.calledOnce, 'to be true'))
        .then(() => expect(which.lastCall.args[0], 'to equal', 'asdf'));
    });

    it('should call #spawn with which results and other args', function () {
      return expect(
        External.run('asdf', [], {
          onspawn() {
            stubProcess.emit('close', 0);
          },
        }),
        'to be fulfilled'
      )
        .then(() => expect(which.calledOnce, 'to be true'))
        .then(() => expect(which.lastCall.args[0], 'to equal', 'asdf'));
    });
  });
});