Home Manual Reference Source Test

test/BrokenLinkChecker.spec.js

import { createServer, Server } from 'http';
import { join } from 'path';
import expect from 'unexpected';
import { spy } from 'sinon';
import request from 'supertest';
import getPort from 'get-port';
import BrokenLinkChecker from '../src/BrokenLinkChecker';

/** @test {BrokenLinkChecker} */
describe('BrokenLinkChecker', function() {
  /** @test {BrokenLinkChecker#startServer} */
  describe('#startServer', function() {
    const checker = new BrokenLinkChecker();
    checker.path = __dirname;

    it('should fail with invalid port', function() {
      return expect(checker.startServer(-13), 'to be rejected with', /^Port should be/);
    });

    it('should create instance of express', function() {
      return getPort()
        .then(port => checker.startServer(port))
        .then(() => {
          expect(checker.app, 'to be a', 'function');
        });
    });

    it('should create instance of http.Server', function() {
      return getPort()
        .then(port => checker.startServer(port))
        .then(() => {
          expect(checker.server, 'to be a', Server);
        });
    });

    it('should serve on custom baseUrl if passed', async function() {
      checker.options = { baseUrl: '/test/' };

      const port = await getPort();
      await checker.startServer(port);

      const index = await request(checker.app).get('/');
      expect(index.status, 'to equal', 404);

      const customIndex = await request(checker.app).get('/test/BrokenLinkChecker.spec.js');
      expect(customIndex.status, 'to equal', 200);
    });

    it('should fail without path', function() {
      checker.path = false;
      return expect(checker.startServer(9000), 'to be rejected with', 'No path given');
    });
  });

  /** @test {BrokenLinkChecker#runChecker} */
  describe('#runChecker', function() {
    let checker;

    this.timeout(5000);

    beforeEach(() => (checker = new BrokenLinkChecker()));

    it('should fail without port and url', function() {
      return expect(checker.runChecker(), 'to be rejected with', 'No url given');
    });

    it('should fail with invalid url', function() {
      checker.url = 'http://localhost:80';
      return expect(checker.runChecker(), 'to be fulfilled with', 1);
    });

    it('should fail with path to invalid document', function() {
      checker.path = join(__dirname, 'fixtures', 'broken');

      return expect(
        getPort()
          .then(port => checker.startServer(port))
          .then(port => checker.runChecker(port)),
        'to be fulfilled with', 1
      );
    });

    it('should work with path to valid document', function() {
      checker.path = join(__dirname, 'fixtures', 'no-broken');

      return expect(
        getPort()
          .then(port => checker.startServer(port))
          .then(port => checker.runChecker(port)),
        'to be fulfilled with', 0
      );
    });
  });

  /** @test {BrokenLinkChecker#validateOptions} */
  describe('#validateOptions', function() {
    it('should fail without non-optional argument', function() {
      const checker = new BrokenLinkChecker();

      return expect(checker.validateOptions(), 'to be rejected with',
        'Neither directory nor url given'
      );
    });

    it('should fail with multiple non-optional arguments ', function() {
      const checker = new BrokenLinkChecker(['dir', 'another']);

      return expect(checker.validateOptions(), 'to be rejected with',
        'Too many non-option arguments: got 2, maximum of 1'
      );
    });

    it('should work with single non-optional argument', function() {
      const checker = new BrokenLinkChecker(['dir']);

      return expect(checker.validateOptions(), 'when fulfilled', 'to be a', 'object');
    });

    it('should add leading slash to base-url if needed', async function() {
      const checker = new BrokenLinkChecker(['dir', '--base-url', 'asdf']);

      await expect(checker.validateOptions(), 'when fulfilled', 'to be a', 'object');
      expect(checker.baseUrl, 'to equal', '/asdf');
    });
  });

  /** @test {BrokenLinkChecker#getPathOrUrl} */
  describe('#getPathOrUrl', function() {
    it('should set url if a url is passed', function() {
      const checker = new BrokenLinkChecker(['http://google.com']);

      return checker.validateOptions()
        .then(() => checker.getPathOrUrl())
        .then(() => expect(checker.url, 'to equal', 'http://google.com'));
    });

    it('should set path if a path is passed', function() {
      const checker = new BrokenLinkChecker(['./directory']);

      return checker.validateOptions()
        .then(() => checker.getPathOrUrl())
        .then(() => expect(checker.path, 'to equal', join(process.cwd(), 'directory')));
    });
  });

  /** @test {BrokenLinkChecker#exit} */
  describe('#exit', function() {
    let checker;
    let consoleError;

    before(function() {
      consoleError = spy(console, 'error');
    });

    beforeEach(function() {
      checker = new BrokenLinkChecker();
    });

    it('should set exitCode', function() {
      checker.exit(123);
      expect(process.exitCode, 'to equal', 123);
    });

    it('should report error', function() {
      const errorMessage = 'an error';

      checker.exit(1, new Error(errorMessage));
      expect(consoleError.calledOnce, 'to be', true);
      expect(consoleError.lastCall.args[0], 'to contain', errorMessage);
    });

    it('should close server', function(done) {
      checker.server = createServer();
      checker.server.on('close', done);

      checker.exit(1);
    });

    after(function() {
      console.error.restore(); // eslint-disable-line no-console
    });
  });

  /** @test {BrokenLinkChecker#launch} */
  describe('#launch', function() {
    this.timeout(5000);

    it('should fail with invalid directory', function() {
      const checker = new BrokenLinkChecker([join('test', 'fixtures', 'broken')]);

      return expect(checker.launch(), 'to be fulfilled with', 1);
    });

    it('should work with valid directory', function() {
      const checker = new BrokenLinkChecker([join('test', 'fixtures', 'no-broken')]);

      return expect(checker.launch(), 'to be fulfilled with', 0);
    });

    it('should fail with invalid url', function() {
      const checker = new BrokenLinkChecker(['htt://ls-age.com']);

      return expect(checker.launch(), 'to be fulfilled with', 1);
    });

    it('should work with valid url', function() {
      const checker = new BrokenLinkChecker([join('test', 'fixtures', 'no-broken')]);

      return expect(
        getPort()
          .then(() => checker.validateOptions())
          .then(() => checker.getPathOrUrl())
          .then(getPort)
          .then(port => checker.startServer(port))
          .then(port => (new BrokenLinkChecker([`http://localhost:${port}`])).launch()),
        'to be fulfilled with', 0
      );
    });
  });
});