<commit_before>import 'babel-polyfill';
import fs from 'fs';
import nock from 'nock';
import request from 'supertest';
import { expect } from 'chai';
import { spy as spyOn } from 'sinon';
import { start, close } from '../src/mock_api.js';

const PROD_ROOT_URL = 'http://localhost:4444';
const RESPONSES_DIR = `${__dirname}/responses`;
const JSONP_CALLBACK = '?callback=test';
const IGNORED_QUERY_PARAMS = '?queryStringIgnore=test';
const DEFAULT_OPTIONS = {
  prodRootURL: PROD_ROOT_URL,
  fixturesPath: RESPONSES_DIR,
  queryStringIgnore: [
    new RegExp(`\\${IGNORED_QUERY_PARAMS}$`)
  ],
  quiet: true
};

describe('start()', function() {
  describe('Initialization', function() {
    it('calls the passed in callback with an error when fixturesPath is not specified', function(done) {
      start({
        prodRootURL: 'http://www.refinery29.com'
      }, (err) => {
        expect(err).to.be.an('error');
        done();
      });
    });

    it('calls the passed in callback with an error when prodRootUrl is not specified', function(done) {
      start({
        fixturesPath: './fixtures'
      }, (err) => {
        expect(err).to.be.an('error');
        done();
      });
    });

    it('does not pass in an error, and populates result.app and result.servers when prodRootURL and fixturesPath are specified', function(done) {
      start({
        prodRootURL: 'http://www.refinery29.com',
        fixturesPath: './fixtures'
      }, (err, result) => {
        expect(err).to.not.exist;

        const { app, servers } = result;
        expect(app).to.be.a('function');
        expect(servers).to.have.length(1);

        const [ server ] = servers;
        expect(server.port).to.equal(4567);
        expect(server.server).to.be.a('object');
        expect(server.active).to.be.true;

        close(servers, done);
      });
    });
  });

  describe('Handling routes', function() {
    describe('When there is no override', function() {
      describe('And there is no response matching a given route', function() {
        let mockAPI;
        const route = '/non_persisted_json_route';
        const response = { source: 'Remote API' };
        const responsePath = RESPONSES_DIR + route + '.json';
        const responsePathWithCallback = RESPONSES_DIR + route + JSONP_CALLBACK + '.json';

        describe('When the request method is GET', function() {

          describe('And the saveFixtures setting is set to true', function() {
            beforeEach(function(done) {
              nock(PROD_ROOT_URL)
                .get(route)
                .query(true)
                .reply(200, response);

              start(DEFAULT_OPTIONS, (err, result) => {
                mockAPI = result;
                done();
              });
            });

            afterEach(function() {
              close(mockAPI.servers);
              [responsePath, responsePathWithCallback].forEach(path => {
                try {
                  fs.accessSync(path, fs.F_OK);
                } catch (e) {
                  return;
                }
                fs.unlinkSync(path);
              });
            });

            it('persists and responds with a response from the production API', function(done) {
              request(mockAPI.app)
                .get(route)
                .expect('Content-Type', /application\/json/)
                .expect(200, response, () => fs.access(responsePath, fs.F_OK, done));
            });

            it('truncates ignored query string expressions in the persisted response filename', function(done) {
              request(mockAPI.app)
                .get(route + IGNORED_QUERY_PARAMS)
                .expect('Content-Type', /application\/json/)
                .expect(200, response, () => fs.access(responsePath, fs.F_OK, done));
            });

            it('renders the endpoint as JSONP when a callback is specified in the query string', function(done) {
              request(mockAPI.app)
                .get(route + JSONP_CALLBACK)
                .expect('Content-Type', /application\/javascript/)
                .expect(200, response, () => fs.access(responsePathWithCallback, fs.F_OK, done));
            });
          });

          describe('And the saveFixtures setting is set to false', function() {
            beforeEach(function(done) {
              nock(PROD_ROOT_URL)
                .get(route)
                .query(true)
                .reply(200, response);

              start({ ...DEFAULT_OPTIONS, saveFixtures: false }, (err, result) => {
                mockAPI = result;
                done();
              });
            });

            afterEach(function() {
              close(mockAPI.servers);
            });

            it('responses with a response from the production API and does not persist the response', function(done) {
              request(mockAPI.app)
                .get(route)
                .expect('Content-Type', /application\/json/)
                .expect(200)
                .end((err, res) => {
                  expect(res.text).to.equal(JSON.stringify(response));
                  fs.access(responsePath, fs.F_OK, (err) => {
                    if (err) {
                      done();
                    }
                  });
                });
            });
          });
        });

        describe('When the request method is not GET', function() {
          beforeEach(function(done) {
            start(DEFAULT_OPTIONS, (err, result) => {
              mockAPI = result;
              done();
            });
          });

          afterEach(function() {
            close(mockAPI.servers);
          });

          it('does not call the production API and returns an error', function(done) {
            request(mockAPI.app)
              .put(route)
              .expect(500, '', done);
          });
        });
      });

      describe('And there is a JSON response matching a given route', function() {
        let mockAPI;
        const route = '/persisted_json_route';
        const responsePath = RESPONSES_DIR + route + '.json';
        const responsePathWithCallback = RESPONSES_DIR + route + JSONP_CALLBACK + '.json';
        const jsonResponse = JSON.parse(fs.readFileSync(responsePath, 'utf8'));
        const jsonpResponse = fs.readFileSync(responsePathWithCallback, 'utf8');

        beforeEach(function(done) {
          nock(PROD_ROOT_URL)
            .get(route)
            .query(true)
            .replyWithError('Fake API hit the production API');

          start(DEFAULT_OPTIONS, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        afterEach(function() {
          close(mockAPI.servers);
        });

        it('serves the locally persisted response as JSON and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route)
            .expect('Content-Type', /application\/json/)
            .expect(200, jsonResponse, done);
        });

        it('ignores truncated query string expressions when identifying the persisted response filename and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route + IGNORED_QUERY_PARAMS)
            .expect('Content-Type', /application\/json/)
            .expect(200, jsonResponse, done);
        });

        it('renders the endpoint as JSONP when a callback is specified in the query string and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route + JSONP_CALLBACK)
            .expect('Content-Type', /application\/javascript/)
            .expect(200, jsonpResponse, done);
        });
      });

      describe('And there is a non-JSON response matching a given route', function() {
        let mockAPI;
        const route = '/persisted_html_route';
        const responsePath = RESPONSES_DIR + route + '.json';
        const response = fs.readFileSync(responsePath, 'utf8');

        before(function(done) {
          nock(PROD_ROOT_URL)
            .get(route)
            .replyWithError('Fake API hit the production API');

          start(DEFAULT_OPTIONS, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        after(function() {
          close(mockAPI.servers);
        });

        it('responds with the locally persisted response as `text/html` and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route)
            .expect('Content-Type', /text\/html/)
            .expect(200, response, done);
        });
      });
    });

    describe('When there is an override for a given route', function() {
      describe('And there is a response with header params specified in the override', function() {
        let mockAPI;
        const route = '/overridden_route';
        const response = 'overridden response';
        const modOptions = {
          ...DEFAULT_OPTIONS,
          overrides: {
            get: [
              {
                route,
                response,
                status: 503,
                headers: { 'Content-Type': 'text/plain' }
              }
            ]
          }
        };

        before(function(done) {
          nock(PROD_ROOT_URL)
            .get(route)
            .query(true)
            .replyWithError('Fake API hit the production API');

          start(modOptions, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        after(function() {
          close(mockAPI.servers);
        });

        it('responds with the specified response, status, and headers and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route)
            .expect('Content-Type', /text\/plain/)
            .expect(503, response, done);
        });

        it('responds with the specified headers even if the filename specifies a JSONP callback', function(done) {
          request(mockAPI.app)
            .get(route + JSONP_CALLBACK)
            .expect('Content-Type', /text\/plain/, done);
        });
      });

      describe('And there is a JSON response with no header params specified in the override', function() {
        let mockAPI;
        const route = '/overridden_route';
        const response = { status: 'overridden response' };
        const modOptions = {
          ...DEFAULT_OPTIONS,
          overrides: {
            get: [
              {
                route,
                response
              }
            ]
          }
        };

        before(function(done) {
          nock(PROD_ROOT_URL)
            .get(route)
            .replyWithError('Fake API hit the production API');

          start(modOptions, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        after(function() {
          close(mockAPI.servers);
        });

        it('responds with the specified response rendered as JSON, status 200, and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route)
            .expect('Content-Type', /application\/json/)
            .expect(200, response, done);
        });
      });

      describe('And there is a JSON response with query param expectations specified in the override', function() {
        let mockAPI;
        const route = '/overridden_route';
        const response = { status: 'overridden response' };

        const modOptions = {
          ...DEFAULT_OPTIONS,
          overrides: {
            get: [
              {
                route,
                response,
                withQueryParams: { foo: 'bar' }
              }
            ]
          }
        };

        before(function(done) {
          nock(PROD_ROOT_URL)
            .get(route)
            .replyWithError('Fake API hit the production API');

          start(modOptions, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        after(function() {
          close(mockAPI.servers);
        });

        describe('when the query params are specified in the route', function() {
          it('responds with the specified response, status 200, and does not hit the production API', function(done) {
            request(mockAPI.app)
              .get(route + '?foo=bar')
              .expect('Content-Type', /application\/json/)
              .expect(200, response, done);
          });
        });

        describe('when the query params are not specified in the route', function() {
          it('does not respond with the specified response, status 200, and does not hit the production API', function(done) {
            request(mockAPI.app)
              .get(route + '?foo=quux')
              .expect(500, '', done);
          });
        });
      });

      describe('And there is a JSON response with a mergeParmas callback specified in the override', function() {
        let mockAPI;
        const route = '/overridden_route';
        const reqBody = { merged: 'merged body' };
        const response = { status: 'overridden response' };
        const modOptions = {
          ...DEFAULT_OPTIONS,
          overrides: {
            post: [
              {
                route,
                response,
                mergeParams: (response, params) => ({ ...response, ...params })
              }
            ]
          }
        };

        before(function(done) {
          nock(PROD_ROOT_URL)
            .post(route)
            .replyWithError('Fake API hit the production API');

          start(modOptions, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        after(function() {
          close(mockAPI.servers);
        });

        it('responds with the specified response rendered as JSON, status 200, and does not hit the production API', function(done) {
          request(mockAPI.app)
            .post(route)
            .send(reqBody)
            .expect('Content-Type', /application\/json/)
            .expect(200, { ...response, ...reqBody }, done);
        });
      });
    });
  });
});

describe('close()', function() {
  it('calls the callback with an error if there are no servers', function(done) {
    close(null, (err) => {
      expect(err).to.be.an('error');
      done();
    });
  });

  describe('when there are only inactive servers', function() {
    before(function(done) {
      const ports = [5000, 5001, 5002];
      const modOptions = { ...DEFAULT_OPTIONS, ports };
      start(modOptions, (err, result) => {
        close(result.servers, done);
      });
    });

    it('calls the callback with an error if there are only inactive servers', function(done) {
      close(null, (err) => {
        expect(err).to.be.an('error');
        done();
      });
    });
  });

  describe('without explicitly passing in servers', function() {
    let ports, servers;

    beforeEach(function(done) {
      ports = [1111, 1112, 1113];
      const modOptions = { ...DEFAULT_OPTIONS, ports };
      start(modOptions, (err, result) => {
        servers = result.servers;
        done();
      });
    });

    it('marks all running servers as inactive', function(done) {
      close(null, () => {
        expect(servers.filter(server => server.active)).to.have.length(0);
        done();
      });
    });
  });

  describe('explicitly passing in servers', function() {
    it('marks all servers passed to it as inactive', function(done) {
      const mockServer = {
        close(callback) {
          return callback();
        }
      };
      spyOn(mockServer, 'close');
      const servers = [
        {
          port: 4567,
          server: mockServer,
          active: true
        }
      ];
      close(servers, () => {
        expect(mockServer.close).to.have.been.called;
        expect(servers.filter(server => server.active)).to.have.length(0);
        done();
      });
    });
  });
});
<commit_msg>Prefer `end` for these fs-linked routes<commit_after>import 'babel-polyfill';
import fs from 'fs';
import nock from 'nock';
import request from 'supertest';
import { expect } from 'chai';
import { spy as spyOn } from 'sinon';
import { start, close } from '../src/mock_api.js';

const PROD_ROOT_URL = 'http://localhost:4444';
const RESPONSES_DIR = `${__dirname}/responses`;
const JSONP_CALLBACK = '?callback=test';
const IGNORED_QUERY_PARAMS = '?queryStringIgnore=test';
const DEFAULT_OPTIONS = {
  prodRootURL: PROD_ROOT_URL,
  fixturesPath: RESPONSES_DIR,
  queryStringIgnore: [
    new RegExp(`\\${IGNORED_QUERY_PARAMS}$`)
  ],
  quiet: true
};

describe('start()', function() {
  describe('Initialization', function() {
    it('calls the passed in callback with an error when fixturesPath is not specified', function(done) {
      start({
        prodRootURL: 'http://www.refinery29.com'
      }, (err) => {
        expect(err).to.be.an('error');
        done();
      });
    });

    it('calls the passed in callback with an error when prodRootUrl is not specified', function(done) {
      start({
        fixturesPath: './fixtures'
      }, (err) => {
        expect(err).to.be.an('error');
        done();
      });
    });

    it('does not pass in an error, and populates result.app and result.servers when prodRootURL and fixturesPath are specified', function(done) {
      start({
        prodRootURL: 'http://www.refinery29.com',
        fixturesPath: './fixtures'
      }, (err, result) => {
        expect(err).to.not.exist;

        const { app, servers } = result;
        expect(app).to.be.a('function');
        expect(servers).to.have.length(1);

        const [ server ] = servers;
        expect(server.port).to.equal(4567);
        expect(server.server).to.be.a('object');
        expect(server.active).to.be.true;

        close(servers, done);
      });
    });
  });

  describe('Handling routes', function() {
    describe('When there is no override', function() {
      describe('And there is no response matching a given route', function() {
        let mockAPI;
        const route = '/non_persisted_json_route';
        const response = { source: 'Remote API' };
        const responsePath = RESPONSES_DIR + route + '.json';
        const responsePathWithCallback = RESPONSES_DIR + route + JSONP_CALLBACK + '.json';

        describe('When the request method is GET', function() {
          describe('And the saveFixtures setting is set to true', function() {
            beforeEach(function(done) {
              nock(PROD_ROOT_URL)
                .get(route)
                .query(true)
                .reply(200, response);

              start(DEFAULT_OPTIONS, (err, result) => {
                mockAPI = result;
                done();
              });
            });

            afterEach(function() {
              close(mockAPI.servers);
              [responsePath, responsePathWithCallback].forEach(path => {
                try {
                  fs.accessSync(path, fs.F_OK);
                } catch (e) {
                  return;
                }
                fs.unlinkSync(path);
              });
            });

            it('persists and responds with a response from the production API', function(done) {
              request(mockAPI.app)
                .get(route)
                .expect('Content-Type', /application\/json/)
                .expect(200)
                .end((err, res) => {
                  expect(res.text).to.equal(JSON.stringify(response));
                  fs.access(responsePath, fs.F_OK, done);
                });
            });

            it('truncates ignored query string expressions in the persisted response filename', function(done) {
              request(mockAPI.app)
                .get(route + IGNORED_QUERY_PARAMS)
                .expect('Content-Type', /application\/json/)
                .expect(200)
                .end((err, res) => {
                  expect(res.text).to.equal(JSON.stringify(response));
                  fs.access(responsePath, fs.F_OK, done);
                });
            });

            it('renders the endpoint as JSONP when a callback is specified in the query string', function(done) {
              request(mockAPI.app)
                .get(route + JSONP_CALLBACK)
                .expect('Content-Type', /application\/javascript/)
                .expect(200)
                .end((err, res) => {
                  expect(res.text).to.equal(JSON.stringify(response));
                  fs.access(responsePathWithCallback, fs.F_OK, done);
                });
            });
          });

          describe('And the saveFixtures setting is set to false', function() {
            beforeEach(function(done) {
              nock(PROD_ROOT_URL)
                .get(route)
                .query(true)
                .reply(200, response);

              start({ ...DEFAULT_OPTIONS, saveFixtures: false }, (err, result) => {
                mockAPI = result;
                done();
              });
            });

            afterEach(function() {
              close(mockAPI.servers);
            });

            it('responses with a response from the production API and does not persist the response', function(done) {
              request(mockAPI.app)
                .get(route)
                .expect('Content-Type', /application\/json/)
                .expect(200)
                .end((err, res) => {
                  expect(res.text).to.equal(JSON.stringify(response));
                  fs.access(responsePath, fs.F_OK, (err) => {
                    if (err) {
                      return done();
                    }
                    throw new Error(
                      'Saved a fixture with saveFixtures set to false'
                    );
                  });
                });
            });
          });
        });

        describe('When the request method is not GET', function() {
          beforeEach(function(done) {
            start(DEFAULT_OPTIONS, (err, result) => {
              mockAPI = result;
              done();
            });
          });

          afterEach(function() {
            close(mockAPI.servers);
          });

          it('does not call the production API and returns an error', function(done) {
            request(mockAPI.app)
              .put(route)
              .expect(500, '', done);
          });
        });
      });

      describe('And there is a JSON response matching a given route', function() {
        let mockAPI;
        const route = '/persisted_json_route';
        const responsePath = RESPONSES_DIR + route + '.json';
        const responsePathWithCallback = RESPONSES_DIR + route + JSONP_CALLBACK + '.json';
        const jsonResponse = JSON.parse(fs.readFileSync(responsePath, 'utf8'));
        const jsonpResponse = fs.readFileSync(responsePathWithCallback, 'utf8');

        beforeEach(function(done) {
          nock(PROD_ROOT_URL)
            .get(route)
            .query(true)
            .replyWithError('Fake API hit the production API');

          start(DEFAULT_OPTIONS, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        afterEach(function() {
          close(mockAPI.servers);
        });

        it('serves the locally persisted response as JSON and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route)
            .expect('Content-Type', /application\/json/)
            .expect(200, jsonResponse, done);
        });

        it('ignores truncated query string expressions when identifying the persisted response filename and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route + IGNORED_QUERY_PARAMS)
            .expect('Content-Type', /application\/json/)
            .expect(200, jsonResponse, done);
        });

        it('renders the endpoint as JSONP when a callback is specified in the query string and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route + JSONP_CALLBACK)
            .expect('Content-Type', /application\/javascript/)
            .expect(200, jsonpResponse, done);
        });
      });

      describe('And there is a non-JSON response matching a given route', function() {
        let mockAPI;
        const route = '/persisted_html_route';
        const responsePath = RESPONSES_DIR + route + '.json';
        const response = fs.readFileSync(responsePath, 'utf8');

        before(function(done) {
          nock(PROD_ROOT_URL)
            .get(route)
            .replyWithError('Fake API hit the production API');

          start(DEFAULT_OPTIONS, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        after(function() {
          close(mockAPI.servers);
        });

        it('responds with the locally persisted response as `text/html` and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route)
            .expect('Content-Type', /text\/html/)
            .expect(200, response, done);
        });
      });
    });

    describe('When there is an override for a given route', function() {
      describe('And there is a response with header params specified in the override', function() {
        let mockAPI;
        const route = '/overridden_route';
        const response = 'overridden response';
        const modOptions = {
          ...DEFAULT_OPTIONS,
          overrides: {
            get: [
              {
                route,
                response,
                status: 503,
                headers: { 'Content-Type': 'text/plain' }
              }
            ]
          }
        };

        before(function(done) {
          nock(PROD_ROOT_URL)
            .get(route)
            .query(true)
            .replyWithError('Fake API hit the production API');

          start(modOptions, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        after(function() {
          close(mockAPI.servers);
        });

        it('responds with the specified response, status, and headers and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route)
            .expect('Content-Type', /text\/plain/)
            .expect(503, response, done);
        });

        it('responds with the specified headers even if the filename specifies a JSONP callback', function(done) {
          request(mockAPI.app)
            .get(route + JSONP_CALLBACK)
            .expect('Content-Type', /text\/plain/, done);
        });
      });

      describe('And there is a JSON response with no header params specified in the override', function() {
        let mockAPI;
        const route = '/overridden_route';
        const response = { status: 'overridden response' };
        const modOptions = {
          ...DEFAULT_OPTIONS,
          overrides: {
            get: [
              {
                route,
                response
              }
            ]
          }
        };

        before(function(done) {
          nock(PROD_ROOT_URL)
            .get(route)
            .replyWithError('Fake API hit the production API');

          start(modOptions, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        after(function() {
          close(mockAPI.servers);
        });

        it('responds with the specified response rendered as JSON, status 200, and does not hit the production API', function(done) {
          request(mockAPI.app)
            .get(route)
            .expect('Content-Type', /application\/json/)
            .expect(200, response, done);
        });
      });

      describe('And there is a JSON response with query param expectations specified in the override', function() {
        let mockAPI;
        const route = '/overridden_route';
        const response = { status: 'overridden response' };

        const modOptions = {
          ...DEFAULT_OPTIONS,
          overrides: {
            get: [
              {
                route,
                response,
                withQueryParams: { foo: 'bar' }
              }
            ]
          }
        };

        before(function(done) {
          nock(PROD_ROOT_URL)
            .get(route)
            .replyWithError('Fake API hit the production API');

          start(modOptions, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        after(function() {
          close(mockAPI.servers);
        });

        describe('when the query params are specified in the route', function() {
          it('responds with the specified response, status 200, and does not hit the production API', function(done) {
            request(mockAPI.app)
              .get(route + '?foo=bar')
              .expect('Content-Type', /application\/json/)
              .expect(200, response, done);
          });
        });

        describe('when the query params are not specified in the route', function() {
          it('does not respond with the specified response, status 200, and does not hit the production API', function(done) {
            request(mockAPI.app)
              .get(route + '?foo=quux')
              .expect(500, '', done);
          });
        });
      });

      describe('And there is a JSON response with a mergeParmas callback specified in the override', function() {
        let mockAPI;
        const route = '/overridden_route';
        const reqBody = { merged: 'merged body' };
        const response = { status: 'overridden response' };
        const modOptions = {
          ...DEFAULT_OPTIONS,
          overrides: {
            post: [
              {
                route,
                response,
                mergeParams: (response, params) => ({ ...response, ...params })
              }
            ]
          }
        };

        before(function(done) {
          nock(PROD_ROOT_URL)
            .post(route)
            .replyWithError('Fake API hit the production API');

          start(modOptions, (err, result) => {
            mockAPI = result;
            done();
          });
        });

        after(function() {
          close(mockAPI.servers);
        });

        it('responds with the specified response rendered as JSON, status 200, and does not hit the production API', function(done) {
          request(mockAPI.app)
            .post(route)
            .send(reqBody)
            .expect('Content-Type', /application\/json/)
            .expect(200, { ...response, ...reqBody }, done);
        });
      });
    });
  });
});

describe('close()', function() {
  it('calls the callback with an error if there are no servers', function(done) {
    close(null, (err) => {
      expect(err).to.be.an('error');
      done();
    });
  });

  describe('when there are only inactive servers', function() {
    before(function(done) {
      const ports = [5000, 5001, 5002];
      const modOptions = { ...DEFAULT_OPTIONS, ports };
      start(modOptions, (err, result) => {
        close(result.servers, done);
      });
    });

    it('calls the callback with an error if there are only inactive servers', function(done) {
      close(null, (err) => {
        expect(err).to.be.an('error');
        done();
      });
    });
  });

  describe('without explicitly passing in servers', function() {
    let ports, servers;

    beforeEach(function(done) {
      ports = [1111, 1112, 1113];
      const modOptions = { ...DEFAULT_OPTIONS, ports };
      start(modOptions, (err, result) => {
        servers = result.servers;
        done();
      });
    });

    it('marks all running servers as inactive', function(done) {
      close(null, () => {
        expect(servers.filter(server => server.active)).to.have.length(0);
        done();
      });
    });
  });

  describe('explicitly passing in servers', function() {
    it('marks all servers passed to it as inactive', function(done) {
      const mockServer = {
        close(callback) {
          return callback();
        }
      };
      spyOn(mockServer, 'close');
      const servers = [
        {
          port: 4567,
          server: mockServer,
          active: true
        }
      ];
      close(servers, () => {
        expect(mockServer.close).to.have.been.called;
        expect(servers.filter(server => server.active)).to.have.length(0);
        done();
      });
    });
  });
});
<|endoftext|>


<commit_before>/**
 * Copyright 2015-present Tuan Le.
 *
 * Licensed under the MIT License.
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/mit-license.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *------------------------------------------------------------------------
 *
 * @module ReactComponentComposite
 * @description - A React component interface factory composite.
 *
 * @author Tuan Le (tuan.t.lei@gmail.com)
 */
/* @flow */
'use strict'; // eslint-disable-line

import CommonElement from '../../core/elements/common-element';

import CompositeElement from '../../core/elements/composite-element';

const DEFAULT_COMPONENT_FN_PREFIX_INCLUSIONS = [
    /* interface reserved method prefixes */
    `on`,
    `do`,
    `handle`,
    `animate`,
    `render`
];

const DEFAULT_COMPONENT_FN_AND_PROPERTY_INCLUSIONS = [
    `outgoing`,
    `getComponentComposites`,
    `getInterfaceComposites`
];

const Hf = CommonElement();

export default CompositeElement({
    template: {
        /**
         * @description - Initialized and check that factory is valid for this composite.
         *
         * @method $initReactComponentComposite
         * @return void
         */
        $initReactComponentComposite () {
            const intf = this;
            if (Hf.DEVELOPMENT) {
                if (!Hf.isSchema({
                    name: `string`,
                    isStateless: `function`,
                    incoming: `function`,
                    outgoing: `function`,
                    reduceState: `function`,
                    getStateCursor: `function`,
                    getStateAsObject: `function`,
                    getComponentLib: `function`,
                    getInitialReflectedState: `function`
                }).of(intf)) {
                    Hf.log(`error`, `ReactComponentComposite.$init - Interface is invalid. Cannot apply composite.`);
                } else {
                    if (Hf.isSchema({
                        getDefaultProps: `function|undefined`,
                        getInitialState: `function|undefined`,
                        setState: `function`,
                        forceUpdate: `function`,
                        componentWillMount: `function`,
                        componentWillReceiveProps: `function`,
                        componentDidMount: `function`,
                        componentWillUpdate: `function`,
                        componentDidUpdate: `function`,
                        componentWillUnmount: `function`,
                        shouldComponentUpdate: `function`
                    }).of(intf)) {
                        Hf.log(`warn1`, `ReactComponentComposite.$init - Interface:${intf.name} should not have internally reverved React lifecyle methods defined.`);
                    }
                }
            }
        },
        /**
         * @description - Handle logic at component after updating stage.
         *
         * @method onCompleteUpdating
         * @param {function} handler
         * @return void
         */
        onCompleteUpdating (handler) {
            const intf = this;
            if (Hf.DEVELOPMENT) {
                if (!Hf.isFunction(handler)) {
                    Hf.log(`error`, `ReactComponentComposite.onCompleteUpdating - Input handler function is invalid.`);
                }
            }
            intf.incoming(`on-component-${intf.name}-${intf.fId}-did-update`).handle((component) => handler(component));
        }
    },
    enclosure: {
        ReactComponentComposite () {
            /* ----- Private Variables ------------- */
            let _mounted = false;
            /* ----- Public Functions -------------- */
            /**
             * @description - Check if interface has a component that is mounted
             *
             * @method isMounted
             * @return {boolean}
             */
            this.isMounted = function () {
                return _mounted;
            };
            /**
             * @description - Handle logic at component mounting stage.
             *
             * @method onMounting
             * @param {function} handler
             * @return void
             */
            this.onMounting = function (handler) {
                const intf = this;
                if (Hf.DEVELOPMENT) {
                    if (!Hf.isFunction(handler)) {
                        Hf.log(`error`, `ReactComponentComposite.onMounting - Input handler function is invalid.`);
                    }
                }
                intf.incoming(`on-component-${intf.name}-${intf.fId}-did-mount`).handle((component) => {
                    _mounted = true;
                    handler(component);
                });
            };
            /**
             * @description - Handle logic at component dismounting stage.
             *
             * @method onDismounting
             * @param {function} handler
             * @return void
             */
            this.onDismounting = function (handler) {
                const intf = this;
                if (Hf.DEVELOPMENT) {
                    if (!Hf.isFunction(handler)) {
                        Hf.log(`error`, `ReactComponentComposite.onDismounting - Input handler function is invalid.`);
                    }
                }
                intf.incoming(`on-component-${intf.name}-${intf.fId}-will-unmount`).handle((component) => {
                    _mounted = false;
                    handler(component);
                });
            };
            /**
             * @description - Convert composite to a renderable component.
             *
             * @method toComponent
             * @param {object} option
             * @returns {object}
             */
            this.toComponent = function (option = {
                alwaysUpdateAsParent: true,
                fnPrefixInclusions: [],
                fnAndPropertyInclusions: []
            }) {
                const intf = this;
                const {
                    React,
                    PropTypes
                } = intf.getComponentLib();
                const {
                    alwaysUpdateAsParent,
                    fnPrefixInclusions,
                    fnAndPropertyInclusions
                } = Hf.fallback({
                    alwaysUpdateAsParent: true,
                    fnPrefixInclusions: [],
                    fnAndPropertyInclusions: []
                }).of(option);
                const reactPropTypeAlias = {
                    boolean: PropTypes.bool,
                    array: PropTypes.array,
                    object: PropTypes.object,
                    function: PropTypes.func,
                    string: PropTypes.string,
                    number: PropTypes.number
                };

                if (intf.isMounted()) {
                    Hf.log(`error`, `ReactComponentComposite.toComponent - Interface:${intf.name} already have a mounted component.`);
                }

                if (Hf.DEVELOPMENT) {
                    if (!Hf.isSchema({
                        Component: `function`
                    }).of(React)) {
                        Hf.log(`error`, `ReactComponentComposite.toComponent - React library is invalid.`);
                    } else if (!Hf.isSchema({
                        bool: `function`,
                        array: `function`,
                        object: `function`,
                        func: `function`,
                        string: `function`,
                        number: `function`,
                        oneOf: `function`,
                        oneOfType: `function`
                    }).of(PropTypes)) {
                        Hf.log(`error`, `ReactComponentComposite.toComponent - React prop-types library is invalid.`);
                    }
                }

                const Component = class Component extends React.Component {
                    // static defaultProps = intf.getStateAsObject();
                    // static propTypes = (() => {
                    //     let propTypes = Object.keys(intf.getStateAsObject()).reduce((propType, key) => {
                    //         if (intf.getStateCursor().isItemStronglyTyped(key)) {
                    //             const propertyTypeAliasKey = Hf.typeOf(intf.getStateAsObject()[key]);
                    //             if (reactPropTypeAlias.hasOwnProperty(propertyTypeAliasKey)) {
                    //                 if (intf.getStateCursor().isItemRequired(key)) {
                    //                     propType[key] = reactPropTypeAlias[propertyTypeAliasKey].isRequired;
                    //                 } else {
                    //                     propType[key] = reactPropTypeAlias[propertyTypeAliasKey];
                    //                 }
                    //             }
                    //         }
                    //         return propType;
                    //     }, {});
                    //
                    //     return Object.keys(intf.getStateAsObject()).reduce((propType, key) => {
                    //         if (intf.getStateCursor().isItemOneOfValues(key)) {
                    //             const {
                    //                 condition: values
                    //             } = intf.getStateCursor().getItemDescription(key).ofConstrainable().getConstraint(`oneOf`);
                    //             propType[key] = PropTypes.oneOf(values);
                    //         }
                    //         if (intf.getStateCursor().isItemOneOfTypes(key)) {
                    //             const {
                    //                 condition: types
                    //             } = intf.getStateCursor().getItemDescription(key).ofConstrainable().getConstraint(`oneTypeOf`);
                    //             propType[key] = PropTypes.oneOfType(types.map((typeAliasKey) => {
                    //                 return reactPropTypeAlias[typeAliasKey];
                    //             }));
                    //         }
                    //         return propType;
                    //     }, propTypes);
                    // })();
                    constructor (props) {
                        super(props);
                        const component = this;
                        component.refCache = {};
                        component.mutationOccurred = false;
                        component.state = !intf.isStateless() ? intf.getInitialReflectedState() : null;

                        Object.entries(Hf.mix(intf, option = {
                            exclusion: {
                                keys: [ `*` ],
                                properties: true,
                                exception: {
                                    prefixes: DEFAULT_COMPONENT_FN_PREFIX_INCLUSIONS.concat(fnPrefixInclusions),
                                    keys: DEFAULT_COMPONENT_FN_AND_PROPERTY_INCLUSIONS.concat(fnAndPropertyInclusions)
                                }
                            }
                        }).with({})).forEach(([ key, value ]) => {
                            if (Hf.isFunction(value)) {
                                const autobind = DEFAULT_COMPONENT_FN_PREFIX_INCLUSIONS.some((prefix) => key.substr(0, prefix.length) === prefix);
                                if (autobind) {
                                    component[key] = value.bind(component);
                                } else {
                                    component[key] = value;
                                }
                            }
                        });
                    }
                    /**
                     * @description - Assign the registered component's reference object.
                     *
                     * @method assignComponentRef
                     * @param {string} refName
                     * @returns function
                     */
                    assignComponentRef (refName) {
                        const component = this;

                        if (Hf.DEVELOPMENT) {
                            if (!Hf.isString(refName)) {
                                Hf.log(`error`, `ReactComponentComposite.assignComponentRef - Input component reference name is invalid.`);
                            }
                        }

                        /* helper function to set component ref */
                        const setComponentRef = function (componentRef) {
                            component.refCache[refName] = Hf.isDefined(componentRef) ? componentRef : null;
                        };
                        return setComponentRef;
                    }
                    /**
                     * @description - Lookup the registered component's reference object.
                     *
                     * @method lookupComponentRefs
                     * @param {array} refNames
                     * @returns {array}
                     */
                    lookupComponentRefs (...refNames) {
                        const component = this;
                        let componentRefs = [];

                        if (Hf.isNonEmptyArray(refNames)) {
                            if (Hf.DEVELOPMENT) {
                                if (!refNames.every((refName) => Hf.isString(refName))) {
                                    Hf.log(`error`, `ReactComponentComposite.lookupComponentRefs - Input component reference name is invalid.`);
                                } else if (!refNames.every((refName) => component.refCache.hasOwnProperty(refName))) {
                                    Hf.log(`warn0`, `ReactComponentComposite.lookupComponentRefs - Component reference is not found.`);
                                    return Array(refNames.length).fill(null);
                                }
                            }

                            componentRefs = Hf.collect(...refNames).from(component.refCache);
                        } else {
                            Hf.log(`error`, `ReactComponentComposite.lookupComponentRefs - Input component reference name array is empty.`);
                        }

                        return componentRefs;
                    }
                    /**
                     * @description - React method for setting up component after mounting.
                     *
                     * @method componentDidMount
                     * @returns void
                     */
                    componentDidMount () {
                        const component = this;

                        if (!intf.isStateless()) {
                            /* this event is call ONLY when the state did mutate in store */
                            intf.incoming(`as-state-mutated`).handle((reflectedState) => {
                                if (Hf.isObject(reflectedState)) {
                                    component.setState(() => reflectedState, () => {
                                        component.mutationOccurred = true;
                                        Hf.log(`info0`, `State mutated for component:${component.props.name}.`);
                                    });
                                }
                            });
                            /* this event is call ONLY when the state did mutate in store and FORCE component to update */
                            intf.incoming(`as-state-forced-to-mutate`).handle((reflectedState) => {
                                if (Hf.isObject(reflectedState)) {
                                    component.setState(() => reflectedState, () => {
                                        component.mutationOccurred = true;
                                        component.forceUpdate();
                                        Hf.log(`info0`, `State mutated for component:${component.props.name}.`);
                                        Hf.log(`info0`, `Forced update for component:${component.props.name}.`);
                                    });
                                }
                            });
                        }

                        // if (alwaysUpdateAsParent && Hf.isDefined(component.props.children)) {
                        if (alwaysUpdateAsParent) {
                            component.mutationOccurred = true;
                        }

                        const defaultProperty = intf.getStateAsObject();

                        /* needs to sync up interface state and component props after mounting */
                        if (intf.reduceState(Hf.fallback(defaultProperty).of(Hf.mix(component.props, {
                            exclusion: {
                                enumerablePropertiesOnly: true,
                                keys: [ `*` ],
                                exception: {
                                    keys: Object.keys(defaultProperty).filter((key) => key !== `name` && key !== `fId` && key !== `intf`)
                                }
                            }
                        }).with({})))) {
                            component.mutationOccurred = true;
                            Hf.log(`info0`, `Property mutated for component:${component.props.name}.`);
                        }
                        component.outgoing(`on-component-${component.props.name}-${component.props.fId}-did-mount`).emit(() => component);
                    }
                    /**
                     * @description - React method for tearing down component before unmounting.
                     *
                     * @method componentWillMount
                     * @returns void
                     */
                    componentWillUnmount () {
                        const component = this;

                        component.outgoing(`on-component-${component.props.name}-${component.props.fId}-will-unmount`).emit(() => component);
                    }
                    /**
                     * @description - React method for preparing component after updating.
                     *
                     * @method componentDidUpdate
                     * @returns void
                     */
                    componentDidUpdate () {
                        const component = this;

                        component.outgoing(`on-component-${component.props.name}-${component.props.fId}-did-update`).emit(() => component);
                    }
                    /**
                     * @description - React method for checking if component should update.
                     *
                     * @method shouldComponentUpdate
                     * @returns {boolean}
                     */
                    shouldComponentUpdate () {
                        const component = this;

                        if (component.mutationOccurred) {
                            component.mutationOccurred = false;
                            Hf.log(`info1`, `Rendering component:${component.props.name}.`);

                            return true;
                        } else { // eslint-disable-line
                            Hf.log(`info1`, `Skipped rendering for component:${component.props.name}.`);

                            return false;
                        }
                    }
                };
                Component.defaultProps = intf.getStateAsObject();
                Component.propTypes = (() => {
                    let propTypes = Object.keys(intf.getStateAsObject()).reduce((propType, key) => {
                        if (intf.getStateCursor().isItemStronglyTyped(key)) {
                            const propertyTypeAliasKey = Hf.typeOf(intf.getStateAsObject()[key]);
                            if (reactPropTypeAlias.hasOwnProperty(propertyTypeAliasKey)) {
                                if (intf.getStateCursor().isItemRequired(key)) {
                                    propType[key] = reactPropTypeAlias[propertyTypeAliasKey].isRequired;
                                } else {
                                    propType[key] = reactPropTypeAlias[propertyTypeAliasKey];
                                }
                            }
                        }
                        return propType;
                    }, {});

                    return Object.keys(intf.getStateAsObject()).reduce((propType, key) => {
                        if (intf.getStateCursor().isItemOneOfValues(key)) {
                            const {
                                condition: values
                            } = intf.getStateCursor().getItemDescription(key).ofConstrainable().getConstraint(`oneOf`);
                            propType[key] = PropTypes.oneOf(values);
                        }
                        if (intf.getStateCursor().isItemOneOfTypes(key)) {
                            const {
                                condition: types
                            } = intf.getStateCursor().getItemDescription(key).ofConstrainable().getConstraint(`oneTypeOf`);
                            propType[key] = PropTypes.oneOfType(types.map((typeAliasKey) => {
                                return reactPropTypeAlias[typeAliasKey];
                            }));
                        }
                        return propType;
                    }, propTypes);
                })();

                if (Hf.DEVELOPMENT) {
                    if (!Hf.isFunction(Component)) {
                        Hf.log(`error`, `ReactComponentComposite.toComponent - Interface:${intf.name} React component is invalid.`);
                    }
                }

                return Component;
            };
        }
    }
});
<commit_msg>Code refactored<commit_after>/**
 * Copyright 2015-present Tuan Le.
 *
 * Licensed under the MIT License.
 * You may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://opensource.org/licenses/mit-license.html
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 *------------------------------------------------------------------------
 *
 * @module ReactComponentComposite
 * @description - A React component interface factory composite.
 *
 * @author Tuan Le (tuan.t.lei@gmail.com)
 */
/* @flow */
'use strict'; // eslint-disable-line

import CommonElement from '../../core/elements/common-element';

import CompositeElement from '../../core/elements/composite-element';

const DEFAULT_COMPONENT_FN_PREFIX_INCLUSIONS = [
    /* interface reserved method prefixes */
    `on`,
    `do`,
    `handle`,
    `animate`,
    `render`
];

const DEFAULT_COMPONENT_FN_INCLUSIONS = [
    `outgoing`,
    `getComponentComposites`,
    `getInterfaceComposites`
];

const DEFAULT_COMPONENT_STATICS = [
    `fId`,
    `contextType`,
    `propTypes`,
    `defaultProps`,
    `router`
];

const Hf = CommonElement();

export default CompositeElement({
    template: {
        /**
         * @description - Initialized and check that factory is valid for this composite.
         *
         * @method $initReactComponentComposite
         * @return void
         */
        $initReactComponentComposite () {
            const intf = this;
            if (Hf.DEVELOPMENT) {
                if (!Hf.isSchema({
                    name: `string`,
                    isStateless: `function`,
                    incoming: `function`,
                    outgoing: `function`,
                    reduceState: `function`,
                    getStateCursor: `function`,
                    getComponentLib: `function`,
                    getInitialReflectedState: `function`
                }).of(intf)) {
                    Hf.log(`error`, `ReactComponentComposite.$init - Interface is invalid. Cannot apply composite.`);
                } else {
                    if (Hf.isSchema({
                        getDefaultProps: `function|undefined`,
                        getInitialState: `function|undefined`,
                        setState: `function`,
                        forceUpdate: `function`,
                        componentWillMount: `function`,
                        componentWillReceiveProps: `function`,
                        componentDidMount: `function`,
                        componentWillUpdate: `function`,
                        componentDidUpdate: `function`,
                        componentWillUnmount: `function`,
                        shouldComponentUpdate: `function`
                    }).of(intf)) {
                        Hf.log(`warn1`, `ReactComponentComposite.$init - Interface:${intf.name} should not have internally reverved React lifecyle methods defined.`);
                    }
                }
            }
        },
        /**
         * @description - Handle logic at component after updating stage.
         *
         * @method onCompleteUpdating
         * @param {function} handler
         * @return void
         */
        onCompleteUpdating (handler) {
            const intf = this;
            if (Hf.DEVELOPMENT) {
                if (!Hf.isFunction(handler)) {
                    Hf.log(`error`, `ReactComponentComposite.onCompleteUpdating - Input handler function is invalid.`);
                }
            }
            intf.incoming(`on-component-${intf.fId}-did-update`).handle((component) => handler(component));
        }
    },
    enclosure: {
        ReactComponentComposite () {
            /* ----- Private Variables ------------- */
            let _mounted = false;
            /* ----- Public Functions -------------- */
            /**
             * @description - Check if interface has a component that is mounted
             *
             * @method isMounted
             * @return {boolean}
             */
            this.isMounted = function () {
                return _mounted;
            };
            /**
             * @description - Handle logic at component mounting stage.
             *
             * @method onMounting
             * @param {function} handler
             * @return void
             */
            this.onMounting = function (handler) {
                const intf = this;
                if (Hf.DEVELOPMENT) {
                    if (!Hf.isFunction(handler)) {
                        Hf.log(`error`, `ReactComponentComposite.onMounting - Input handler function is invalid.`);
                    }
                }
                intf.incoming(`on-component-${intf.fId}-did-mount`).handle((component) => {
                    _mounted = true;
                    handler(component);
                });
            };
            /**
             * @description - Handle logic at component dismounting stage.
             *
             * @method onDismounting
             * @param {function} handler
             * @return void
             */
            this.onDismounting = function (handler) {
                const intf = this;
                if (Hf.DEVELOPMENT) {
                    if (!Hf.isFunction(handler)) {
                        Hf.log(`error`, `ReactComponentComposite.onDismounting - Input handler function is invalid.`);
                    }
                }
                intf.incoming(`on-component-${intf.fId}-will-unmount`).handle((component) => {
                    _mounted = false;
                    handler(component);
                });
            };
            /**
             * @description - Convert composite to a renderable component.
             *
             * @method toComponent
             * @param {object} option
             * @returns {object}
             */
            this.toComponent = function (option = {
                alwaysUpdateAsParent: true,
                fnPrefixInclusions: [],
                fnAndPropertyInclusions: []
            }) {
                const intf = this;
                const {
                    React,
                    PropTypes
                } = intf.getComponentLib();
                const {
                    alwaysUpdateAsParent,
                    fnPrefixInclusions,
                    fnAndPropertyInclusions
                } = Hf.fallback({
                    alwaysUpdateAsParent: true,
                    fnPrefixInclusions: [],
                    fnAndPropertyInclusions: []
                }).of(option);

                if (intf.isMounted()) {
                    Hf.log(`error`, `ReactComponentComposite.toComponent - Interface:${intf.name} already have a mounted component.`);
                }

                if (Hf.DEVELOPMENT) {
                    if (!Hf.isSchema({
                        Component: `function`
                    }).of(React)) {
                        Hf.log(`error`, `ReactComponentComposite.toComponent - React library is invalid.`);
                    } else if (!Hf.isSchema({
                        string: `function`
                    }).of(PropTypes)) {
                        Hf.log(`error`, `ReactComponentComposite.toComponent - React prop-types library is invalid.`);
                    }
                }

                const Component = class Component extends React.Component {
                    constructor (props) {
                        super(props);
                        const component = this;
                        component.refCache = {};
                        component.mutationOccurred = false;
                        component.state = !intf.isStateless() ? intf.getInitialReflectedState() : null;

                        Object.entries(Hf.mix(intf, option = {
                            exclusion: {
                                keys: [ `*` ],
                                properties: true,
                                exception: {
                                    prefixes: DEFAULT_COMPONENT_FN_PREFIX_INCLUSIONS.concat(fnPrefixInclusions),
                                    keys: DEFAULT_COMPONENT_FN_INCLUSIONS.concat(fnAndPropertyInclusions)
                                }
                            }
                        }).with({})).forEach(([ key, value ]) => {
                            if (Hf.isFunction(value)) {
                                const autobind = DEFAULT_COMPONENT_FN_PREFIX_INCLUSIONS.some((prefix) => key.substr(0, prefix.length) === prefix);
                                if (autobind) {
                                    component[key] = value.bind(component);
                                } else {
                                    component[key] = value;
                                }
                            }
                        });
                    }
                    /**
                     * @description - Assign the registered component's reference object.
                     *
                     * @method assignComponentRef
                     * @param {string} refName
                     * @returns function
                     */
                    assignComponentRef (refName) {
                        const component = this;

                        if (Hf.DEVELOPMENT) {
                            if (!Hf.isString(refName)) {
                                Hf.log(`error`, `ReactComponentComposite.assignComponentRef - Input component reference name is invalid.`);
                            }
                        }

                        /* helper function to set component ref */
                        const setComponentRef = function (componentRef) {
                            component.refCache[refName] = Hf.isDefined(componentRef) ? componentRef : null;
                        };
                        return setComponentRef;
                    }
                    /**
                     * @description - Lookup the registered component's reference object.
                     *
                     * @method lookupComponentRefs
                     * @param {array} refNames
                     * @returns {array}
                     */
                    lookupComponentRefs (...refNames) {
                        const component = this;
                        let componentRefs = [];

                        if (Hf.isNonEmptyArray(refNames)) {
                            if (Hf.DEVELOPMENT) {
                                if (!refNames.every((refName) => Hf.isString(refName))) {
                                    Hf.log(`error`, `ReactComponentComposite.lookupComponentRefs - Input component reference name is invalid.`);
                                } else if (!refNames.every((refName) => component.refCache.hasOwnProperty(refName))) {
                                    Hf.log(`warn0`, `ReactComponentComposite.lookupComponentRefs - Component reference is not found.`);
                                    return Array(refNames.length).fill(null);
                                }
                            }

                            componentRefs = Hf.collect(...refNames).from(component.refCache);
                        } else {
                            Hf.log(`error`, `ReactComponentComposite.lookupComponentRefs - Input component reference name array is empty.`);
                        }

                        return componentRefs;
                    }
                    /**
                     * @description - React method for setting up component after mounting.
                     *
                     * @method componentDidMount
                     * @returns void
                     */
                    componentDidMount () {
                        const component = this;

                        if (!intf.isStateless()) {
                            /* this event is call ONLY when the state did mutate in store */
                            intf.incoming(`as-state-mutated`).handle((reflectedState) => {
                                if (Hf.isObject(reflectedState)) {
                                    component.setState(() => reflectedState, () => {
                                        component.mutationOccurred = true;
                                        Hf.log(`info0`, `State mutated for component:${intf.fId}.`);
                                    });
                                }
                            });
                            /* this event is call ONLY when the state did mutate in store and FORCE component to update */
                            intf.incoming(`as-state-forced-to-mutate`).handle((reflectedState) => {
                                if (Hf.isObject(reflectedState)) {
                                    component.setState(() => reflectedState, () => {
                                        component.mutationOccurred = true;
                                        component.forceUpdate();
                                        Hf.log(`info0`, `State mutated for component:${intf.fId}.`);
                                        Hf.log(`info0`, `Forced update for component:${intf.fId}.`);
                                    });
                                }
                            });
                        }

                        component.outgoing(`on-component-${intf.fId}-did-mount`).emit(() => component);
                    }
                    /**
                     * @description - React method for tearing down component before unmounting.
                     *
                     * @method componentWillMount
                     * @returns void
                     */
                    componentWillUnmount () {
                        const component = this;

                        component.outgoing(`on-component-${intf.fId}-will-unmount`).emit(() => component);
                    }
                    /**
                     * @description - React method for preparing component after updating.
                     *
                     * @method componentDidUpdate
                     * @returns void
                     */
                    componentDidUpdate () {
                        const component = this;

                        component.outgoing(`on-component-${intf.fId}-did-update`).emit(() => component);
                    }
                    /**
                     * @description - React method for checking if component should update.
                     *
                     * @method shouldComponentUpdate
                     * @returns {boolean}
                     */
                    shouldComponentUpdate () {
                        const component = this;

                        if (alwaysUpdateAsParent) {
                            Hf.log(`info1`, `Rendering component:${intf.fId}.`);
                        } else {
                            if (component.mutationOccurred) {
                                component.mutationOccurred = false;
                                Hf.log(`info1`, `Rendering component:${intf.fId}.`);
                                return true;
                            } else { // eslint-disable-line
                                Hf.log(`info1`, `Skipped rendering for component:${intf.fId}.`);
                                return false;
                            }
                        }
                        return true;
                    }
                };

                if (Hf.DEVELOPMENT) {
                    if (!Hf.isFunction(Component)) {
                        Hf.log(`error`, `ReactComponentComposite.toComponent - Interface:${intf.name} React component is invalid.`);
                    }
                }

                return DEFAULT_COMPONENT_STATICS.reduce((_Component, componentStatic) => {
                    if (Hf.isObject(Object.getOwnPropertyDescriptor(intf, componentStatic))) {
                        _Component[componentStatic] = intf[componentStatic];
                    }
                    if (componentStatic === `propTypes`) {
                        _Component[componentStatic] = {
                            ..._Component[componentStatic],
                            fId: PropTypes.string
                        };
                    }
                    if (componentStatic === `defaultProps`) {
                        _Component[componentStatic] = {
                            ..._Component[componentStatic],
                            fId: intf.fId
                        };
                    }
                    return _Component;
                }, Component);
            };
        }
    }
});
<|endoftext|>


<commit_before>/*************************************
*	Beckwith                         *
*	github.com/howderek/Beckwith     *
*   (c) 2015 Derek Howard            *
*	Licensed under the MIT license.  *
**************************************/
var Beckwith = (function () {
    //Guts of this function to be injected into a WebWorker using a Blob
    function WorkerCode() {
       // javascript-astar 0.4.0
       // http://github.com/bgrins/javascript-astar
       // Freely distributable under the MIT License.
       // Implements the astar search algorithm in javascript using a Binary Heap.
       // Includes Binary Heap (with modifications) from Marijn Haverbeke.
       // http://eloquentjavascript.net/appendix2.html

        !function(a){if("object"==typeof module&&"object"==typeof module.exports)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b=a();astar=b.astar,Graph=b.Graph}}(function(){function a(a){for(var b=a,c=[];b.parent;)c.push(b),b=b.parent;return c.reverse()}function b(){return new f(function(a){return a.f})}function d(a,b){b=b||{},this.nodes=[],this.diagonal=!!b.diagonal,this.grid=[];for(var c=0;c<a.length;c++){this.grid[c]=[];for(var d=0,f=a[c];d<f.length;d++){var g=new e(c,d,f[d]);this.grid[c][d]=g,this.nodes.push(g)}}this.init()}function e(a,b,c){this.x=a,this.y=b,this.weight=c}function f(a){this.content=[],this.scoreFunction=a}var c={search:function(d,e,f,g){d.cleanDirty(),g=g||{};var h=g.heuristic||c.heuristics.manhattan,i=g.closest||!1,j=b(),k=e;for(e.h=h(e,f),j.push(e);j.size()>0;){var l=j.pop();if(l===f)return a(l);l.closed=!0;for(var m=d.neighbors(l),n=0,o=m.length;o>n;++n){var p=m[n];if(!p.closed&&!p.isWall()){var q=l.g+p.getCost(l),r=p.visited;(!r||q<p.g)&&(p.visited=!0,p.parent=l,p.h=p.h||h(p,f),p.g=q,p.f=p.g+p.h,d.markDirty(p),i&&(p.h<k.h||p.h===k.h&&p.g<k.g)&&(k=p),r?j.rescoreElement(p):j.push(p))}}}return i?a(k):[]},heuristics:{manhattan:function(a,b){var c=Math.abs(b.x-a.x),d=Math.abs(b.y-a.y);return c+d},diagonal:function(a,b){var c=1,d=Math.sqrt(2),e=Math.abs(b.x-a.x),f=Math.abs(b.y-a.y);return c*(e+f)+(d-2*c)*Math.min(e,f)}},cleanNode:function(a){a.f=0,a.g=0,a.h=0,a.visited=!1,a.closed=!1,a.parent=null}};return d.prototype.init=function(){this.dirtyNodes=[];for(var a=0;a<this.nodes.length;a++)c.cleanNode(this.nodes[a])},d.prototype.cleanDirty=function(){for(var a=0;a<this.dirtyNodes.length;a++)c.cleanNode(this.dirtyNodes[a]);this.dirtyNodes=[]},d.prototype.markDirty=function(a){this.dirtyNodes.push(a)},d.prototype.neighbors=function(a){var b=[],c=a.x,d=a.y,e=this.grid;return e[c-1]&&e[c-1][d]&&b.push(e[c-1][d]),e[c+1]&&e[c+1][d]&&b.push(e[c+1][d]),e[c]&&e[c][d-1]&&b.push(e[c][d-1]),e[c]&&e[c][d+1]&&b.push(e[c][d+1]),this.diagonal&&(e[c-1]&&e[c-1][d-1]&&b.push(e[c-1][d-1]),e[c+1]&&e[c+1][d-1]&&b.push(e[c+1][d-1]),e[c-1]&&e[c-1][d+1]&&b.push(e[c-1][d+1]),e[c+1]&&e[c+1][d+1]&&b.push(e[c+1][d+1])),b},d.prototype.toString=function(){for(var c,d,e,f,a=[],b=this.grid,g=0,h=b.length;h>g;g++){for(c=[],d=b[g],e=0,f=d.length;f>e;e++)c.push(d[e].weight);a.push(c.join(" "))}return a.join("\n")},e.prototype.toString=function(){return"["+this.x+" "+this.y+"]"},e.prototype.getCost=function(a){return a&&a.x!=this.x&&a.y!=this.y?1.41421*this.weight:this.weight},e.prototype.isWall=function(){return 0===this.weight},f.prototype={push:function(a){this.content.push(a),this.sinkDown(this.content.length-1)},pop:function(){var a=this.content[0],b=this.content.pop();return this.content.length>0&&(this.content[0]=b,this.bubbleUp(0)),a},remove:function(a){var b=this.content.indexOf(a),c=this.content.pop();b!==this.content.length-1&&(this.content[b]=c,this.scoreFunction(c)<this.scoreFunction(a)?this.sinkDown(b):this.bubbleUp(b))},size:function(){return this.content.length},rescoreElement:function(a){this.sinkDown(this.content.indexOf(a))},sinkDown:function(a){for(var b=this.content[a];a>0;){var c=(a+1>>1)-1,d=this.content[c];if(!(this.scoreFunction(b)<this.scoreFunction(d)))break;this.content[c]=b,this.content[a]=d,a=c}},bubbleUp:function(a){for(var b=this.content.length,c=this.content[a],d=this.scoreFunction(c);;){var h,e=a+1<<1,f=e-1,g=null;if(b>f){var i=this.content[f];h=this.scoreFunction(i),d>h&&(g=f)}if(b>e){var j=this.content[e],k=this.scoreFunction(j);(null===g?d:h)>k&&(g=e)}if(null===g)break;this.content[a]=this.content[g],this.content[g]=c,a=g}}},{astar:c,Graph:d}});
// END OF javascript-astar 0.4.0

        function work(map, startX, startY, endX, endY) {
            var graph = new Graph(map),
                start = graph.grid[startY][startX],
                end = graph.grid[endY][endX];
            postMessage(astar.search(graph, start, end));
        }

        onmessage = function (message) {
            work.apply(this, message.data);
        };

    };

    function Thread(code) {
        this.code = code.toString().substring(code.toString().indexOf('{') + 1, code.toString().lastIndexOf('}'));
        var workerURL = URL.createObjectURL(new Blob([this.code], {
            type: 'text/javascript'
        }));
        this.worker = new Worker(workerURL);
        this.onmessage = function (message) {
            this.currentPromise(message.data);
        }
    };

    Thread.prototype.stop = function () {
        this.worker.terminate();
    };

    Thread.prototype.setCallback = function (func) {
        this.worker.onmessage = func;
    }

    Thread.prototype.call = function () {
        var args = [];
        for (var i = 0; i < arguments.length; i += 1) {
            args.push(arguments[i]);
        }
        this.worker.postMessage(args);
    }

    var Beckwith = function(debug) {
        this.DEBUG = !!(debug);
        this.Thread = new Thread(WorkerCode);
        this.log('Beckwith: Ready for service, sir!');

    };

    Beckwith.prototype = {
        log: function (message, level) {
            var level = level || 'debug';
            if (level === 'error') {
                console.error('Thread: ' + message);
            } else if (level === 'warn') {
                console.error('Thread: ' + message);
            } else if (this.DEBUG) {
                console.log('%c' + message, 'color: #6f3232');
            }
        },
        findPath: function (map, startX, startY, endX, endY) {
            this.log('You: Find us that path!');
            var me = this;

            this.currentPromise = new Promise(
                function (resolve, reject) {
                    var scope = this;
                    me.Thread.setCallback(function (message) {
                        resolve.call(scope, message.data);
                    });
                    me.log('Beckwith: I found the path sir! I\'ll send it now!');
                    me.Thread.call(map, startX, startY, endX, endY);
                }
            );

            return this.currentPromise;
        }
    };

    return Beckwith;

})();
<commit_msg>cleanup and commenting<commit_after>/*************************************
*	Beckwith                         *
*	github.com/howderek/Beckwith     *
*   (c) 2015 Derek Howard            *
*	Licensed under the MIT license.  *
**************************************/
var Beckwith = (function () {
    //Guts of this function to be injected into a WebWorker using a Blob
    function WorkerCode() {
       // javascript-astar 0.4.0
       // http://github.com/bgrins/javascript-astar
       // Freely distributable under the MIT License.
       // Implements the astar search algorithm in javascript using a Binary Heap.
       // Includes Binary Heap (with modifications) from Marijn Haverbeke.
       // http://eloquentjavascript.net/appendix2.html

        !function(a){if("object"==typeof module&&"object"==typeof module.exports)module.exports=a();else if("function"==typeof define&&define.amd)define([],a);else{var b=a();astar=b.astar,Graph=b.Graph}}(function(){function a(a){for(var b=a,c=[];b.parent;)c.push(b),b=b.parent;return c.reverse()}function b(){return new f(function(a){return a.f})}function d(a,b){b=b||{},this.nodes=[],this.diagonal=!!b.diagonal,this.grid=[];for(var c=0;c<a.length;c++){this.grid[c]=[];for(var d=0,f=a[c];d<f.length;d++){var g=new e(c,d,f[d]);this.grid[c][d]=g,this.nodes.push(g)}}this.init()}function e(a,b,c){this.x=a,this.y=b,this.weight=c}function f(a){this.content=[],this.scoreFunction=a}var c={search:function(d,e,f,g){d.cleanDirty(),g=g||{};var h=g.heuristic||c.heuristics.manhattan,i=g.closest||!1,j=b(),k=e;for(e.h=h(e,f),j.push(e);j.size()>0;){var l=j.pop();if(l===f)return a(l);l.closed=!0;for(var m=d.neighbors(l),n=0,o=m.length;o>n;++n){var p=m[n];if(!p.closed&&!p.isWall()){var q=l.g+p.getCost(l),r=p.visited;(!r||q<p.g)&&(p.visited=!0,p.parent=l,p.h=p.h||h(p,f),p.g=q,p.f=p.g+p.h,d.markDirty(p),i&&(p.h<k.h||p.h===k.h&&p.g<k.g)&&(k=p),r?j.rescoreElement(p):j.push(p))}}}return i?a(k):[]},heuristics:{manhattan:function(a,b){var c=Math.abs(b.x-a.x),d=Math.abs(b.y-a.y);return c+d},diagonal:function(a,b){var c=1,d=Math.sqrt(2),e=Math.abs(b.x-a.x),f=Math.abs(b.y-a.y);return c*(e+f)+(d-2*c)*Math.min(e,f)}},cleanNode:function(a){a.f=0,a.g=0,a.h=0,a.visited=!1,a.closed=!1,a.parent=null}};return d.prototype.init=function(){this.dirtyNodes=[];for(var a=0;a<this.nodes.length;a++)c.cleanNode(this.nodes[a])},d.prototype.cleanDirty=function(){for(var a=0;a<this.dirtyNodes.length;a++)c.cleanNode(this.dirtyNodes[a]);this.dirtyNodes=[]},d.prototype.markDirty=function(a){this.dirtyNodes.push(a)},d.prototype.neighbors=function(a){var b=[],c=a.x,d=a.y,e=this.grid;return e[c-1]&&e[c-1][d]&&b.push(e[c-1][d]),e[c+1]&&e[c+1][d]&&b.push(e[c+1][d]),e[c]&&e[c][d-1]&&b.push(e[c][d-1]),e[c]&&e[c][d+1]&&b.push(e[c][d+1]),this.diagonal&&(e[c-1]&&e[c-1][d-1]&&b.push(e[c-1][d-1]),e[c+1]&&e[c+1][d-1]&&b.push(e[c+1][d-1]),e[c-1]&&e[c-1][d+1]&&b.push(e[c-1][d+1]),e[c+1]&&e[c+1][d+1]&&b.push(e[c+1][d+1])),b},d.prototype.toString=function(){for(var c,d,e,f,a=[],b=this.grid,g=0,h=b.length;h>g;g++){for(c=[],d=b[g],e=0,f=d.length;f>e;e++)c.push(d[e].weight);a.push(c.join(" "))}return a.join("\n")},e.prototype.toString=function(){return"["+this.x+" "+this.y+"]"},e.prototype.getCost=function(a){return a&&a.x!=this.x&&a.y!=this.y?1.41421*this.weight:this.weight},e.prototype.isWall=function(){return 0===this.weight},f.prototype={push:function(a){this.content.push(a),this.sinkDown(this.content.length-1)},pop:function(){var a=this.content[0],b=this.content.pop();return this.content.length>0&&(this.content[0]=b,this.bubbleUp(0)),a},remove:function(a){var b=this.content.indexOf(a),c=this.content.pop();b!==this.content.length-1&&(this.content[b]=c,this.scoreFunction(c)<this.scoreFunction(a)?this.sinkDown(b):this.bubbleUp(b))},size:function(){return this.content.length},rescoreElement:function(a){this.sinkDown(this.content.indexOf(a))},sinkDown:function(a){for(var b=this.content[a];a>0;){var c=(a+1>>1)-1,d=this.content[c];if(!(this.scoreFunction(b)<this.scoreFunction(d)))break;this.content[c]=b,this.content[a]=d,a=c}},bubbleUp:function(a){for(var b=this.content.length,c=this.content[a],d=this.scoreFunction(c);;){var h,e=a+1<<1,f=e-1,g=null;if(b>f){var i=this.content[f];h=this.scoreFunction(i),d>h&&(g=f)}if(b>e){var j=this.content[e],k=this.scoreFunction(j);(null===g?d:h)>k&&(g=e)}if(null===g)break;this.content[a]=this.content[g],this.content[g]=c,a=g}}},{astar:c,Graph:d}});
// END OF javascript-astar 0.4.0

        function work(map, startX, startY, endX, endY) {
            var graph = new Graph(map),
                start = graph.grid[startY][startX],
                end = graph.grid[endY][endX];
            postMessage(astar.search(graph, start, end));
        }

        onmessage = function (message) {
            work.apply(this, message.data);
        };

    };

    //Handles creating the web worker
    function Thread(code) {
        //Get the body of the code to inject
        this.code = code.toString().substring(code.toString().indexOf('{') + 1, code.toString().lastIndexOf('}'));
        var workerURL = URL.createObjectURL(new Blob([this.code], {
            type: 'text/javascript'
        }));
        this.worker = new Worker(workerURL);
    };

    //Kills the Web Worker
    Thread.prototype.stop = function () {
        this.worker.terminate();
    };

    //Sets the callback when onmessage is sent from the worker
    Thread.prototype.setCallback = function (func) {
        this.worker.onmessage = func;
    }

    //Handles creating the web worker
    Thread.prototype.call = function () {
        var args = [];
        for (var i = 0; i < arguments.length; i += 1) {
            args.push(arguments[i]);
        }
        this.worker.postMessage(args);
    }

    //constructor, creates the thread and sets the debug flag
    var Beckwith = function(debug) {
        this.DEBUG = !!(debug);
        this.Thread = new Thread(WorkerCode);
        this.log('Beckwith: Ready for service, sir!');

    };

    Beckwith.prototype = {
        log: function (message, level) {
            var level = level || 'debug';
            if (level === 'error') {
                console.error('Thread: ' + message);
            } else if (level === 'warn') {
                console.error('Thread: ' + message);
            } else if (this.DEBUG) {
                console.log('%c' + message, 'color: #6f3232');
            }
        },
        findPath: function (map, startX, startY, endX, endY) {
            this.log('You: Find us that path!');
            var me = this;

            //returns a new promise with the results
            this.currentPromise = new Promise(
                function (resolve, reject) {
                    var scope = this;
                    me.Thread.setCallback(function (message) {
                        resolve.call(scope, message.data);
                    });
                    me.log('Beckwith: I found the path sir! I\'ll send it now!');
                    me.Thread.call(map, startX, startY, endX, endY);
                }
            );

            return this.currentPromise;
        }
    };

    return Beckwith;

})();
<|endoftext|>


<commit_before>const Emitter = require('./Emitter.js')

function update() {
  Emitter.emit('update')
}

const Game = {
  on: (event, fn) => {
    Emitter.subscribe(event, fn)
  },

  run: () => {
    setInterval(update, 500)
  }
}

module.exports = Game
<commit_msg>Adding emit function for custom Game events.<commit_after>const Emitter = require('./Emitter.js')

function update() {
  Emitter.emit('update')
}

const Game = {
  on: (event, fn) => {
    Emitter.subscribe(event, fn)
  },

  emit: (event) =>  {
    Emitter.emit(event)
  },

  run: () => {
    setInterval(update, 500)
  }
}

module.exports = Game
<|endoftext|>


<commit_before>'use strict';

// Read meta data out of each html file in given directory.
// Requires jsdom package installed, and some html files

var fs = require('fs')
  , path = require('path')
  , jsdom = require('jsdom')
  , deferred = require('deferred')

// Convert Node.js async functions, into ones that return a promise
  , promisify = require('deferred').promisify
  , readdir = promisify(fs.readdir)
  , readFile = promisify(fs.readFile)

// Provide path that contains some HTML files
  , root = '/replace/with/valid/path'

  , process, result = {};

process = function (html) {
	var def = deferred();

	// Process HTML with jsdom parser
	jsdom.env({
		html: String(html),
		done: function (errors, window) {
			var data, elems;
			if (errors) {
				def.reject(new Error(errors));
				return;
			}

			data = {};
			// The title is the content of the 1st "h1" element
			elems = window.document.getElementsByTagName('h1');
			if (elems.length) data.title = elems[0].textContent;

			// The description is the content of the 1st "p" element
			elems = window.document.getElementsByTagName('p');
			if (elems.length) data.description = elems[0].textContent;
			def.resolve(data);
		}
	});

	return def.promise;
};

// Read folder
readdir(root).map(function (fileName) {
	// Process only HTML files
	if (path.extname(fileName) !== '.html') return;

	// Read file content
	return readFile(path.resolve(root, fileName))
		// Read meta data out of it
		.then(process)
		// Assign to result
		.aside(function (data) {
			result[fileName] = data;
		});
}).done(function () {
	// Print final result
	console.info("Result:", result);
});
<commit_msg>Replaced `process` which is a Node.js global<commit_after>'use strict';

// Read meta data out of each html file in given directory.
// Requires jsdom package installed, and some html files

var fs = require('fs')
  , path = require('path')
  , jsdom = require('jsdom')
  , deferred = require('deferred')

// Convert Node.js async functions, into ones that return a promise
  , promisify = require('deferred').promisify
  , readdir = promisify(fs.readdir)
  , readFile = promisify(fs.readFile)

// Provide path that contains some HTML files
  , root = '/replace/with/valid/path'

  , extract, result = {};

extract = function (html) {
	var def = deferred();

	// Process HTML with jsdom parser
	jsdom.env({
		html: String(html),
		done: function (errors, window) {
			var data, elems;
			if (errors) {
				def.reject(new Error(errors));
				return;
			}

			data = {};
			// The title is the content of the 1st "h1" element
			elems = window.document.getElementsByTagName('h1');
			if (elems.length) data.title = elems[0].textContent;

			// The description is the content of the 1st "p" element
			elems = window.document.getElementsByTagName('p');
			if (elems.length) data.description = elems[0].textContent;
			def.resolve(data);
		}
	});

	return def.promise;
};

// Read folder
readdir(root).map(function (fileName) {
	// Process only HTML files
	if (path.extname(fileName) !== '.html') return;

	// Read file content
	return readFile(path.resolve(root, fileName))
		// Read meta data out of it
		.then(extract)
		// Assign to result
		.aside(function (data) {
			result[fileName] = data;
		});
}).done(function () {
	// Print final result
	console.info("Result:", result);
});
<|endoftext|>


<commit_before>define([
    'find/app/model/document-model'
], function(DocumentModel) {

    describe('Document model', function() {
        describe('parse method', function() {
            beforeEach(function() {
                this.parse = DocumentModel.prototype.parse;
            });

            it('uses the title from the response if present', function() {
                var title = 'My Document';
                expect(this.parse({reference: 'my-document', title: title}).title).toBe(title);
            });

        });
    });

});
<commit_msg>Fix failing test<commit_after>define([
    'find/app/model/document-model'
], function(DocumentModel) {

    describe('Document model', function() {
        describe('parse method', function() {
            beforeEach(function() {
                this.parse = DocumentModel.prototype.parse;
            });

            it('uses the title from the response if present', function() {
                var title = 'My Document';
                var attributes = {
                    reference: 'my-document',
                    title: title,
                    fieldMap: {
                        url: 'http://example.com'
                    }
                };

                expect(this.parse(attributes).title).toBe(title);
            });

        });
    });

});
<|endoftext|>


<commit_before>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
*/
foam.CLASS({
  package: 'foam.nanos.u2.navigation',
  name: 'UserInfoNavigationView',
  extends: 'foam.u2.View',

  documentation: 'Displays user and agent label if present. Clicking view opens settings submenu.',

  requires: [
    'foam.nanos.menu.Menu',
    'foam.nanos.menu.SubMenuView'
  ],

  imports: [
    'theme',
    'subject'
  ],

  css: `
    ^ {
      cursor: pointer;
      display: flex;
      align-items: center;
    }
    ^carrot {
      margin-left: 15px;
      border-left: 5px solid transparent;
      border-right: 5px solid transparent;
      border-top: 5px solid white;
    }
    ^userName {
      color: /*%GREY4%*/;
      font-weight: 600;
      font-size: 12px;
    }
    ^agentName{
      color: /*%GREY3%*/;
      font-weight: 400;
      font-size: 11px;
    }
    ^ .name-container {
      max-width: 75px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    ^container {
      cursor: pointer;
      display: flex;
      align-items: center;
      width: 100%;
      height: 100%;
    }
  `,

  methods: [
    function initE() {
      this
        .addClass(this.myClass())
        .start().addClass(this.myClass('container'))
          .on('click', () => {
            this.tag(this.SubMenuView.create({
              menu: this.Menu.create({ id: this.theme.settingsRootMenu })
            }));
          })
          .start()
             .add(this.slot((subject$user) => {
              if ( ! this.subject.user ) return;
              return this.E().addClass('name-container')
                  .start('span').addClass(this.myClass('userName'))
                    .add(this.subject.user.toSummary())
                  .end();
            }))
            .add(this.slot((subject$user) => {
              if ( ! this.subject.realUser ) return;
              return this.E().addClass('name-container')
                  .start('span').addClass(this.myClass('agentName'))
                    .add( this.subject.realUser.toSummary() )
                  .end();
            }))
          .end()
          .start()
            .addClass(this.myClass('carrot'))
          .end()
        .end();
    }
  ]
});
<commit_msg>fix the slot<commit_after>/**
 * @license
 * Copyright 2019 The FOAM Authors. All Rights Reserved.
 * http://www.apache.org/licenses/LICENSE-2.0
*/
foam.CLASS({
  package: 'foam.nanos.u2.navigation',
  name: 'UserInfoNavigationView',
  extends: 'foam.u2.View',

  documentation: 'Displays user and agent label if present. Clicking view opens settings submenu.',

  requires: [
    'foam.nanos.menu.Menu',
    'foam.nanos.menu.SubMenuView'
  ],

  imports: [
    'theme',
    'subject'
  ],

  css: `
    ^ {
      cursor: pointer;
      display: flex;
      align-items: center;
    }
    ^carrot {
      margin-left: 15px;
      border-left: 5px solid transparent;
      border-right: 5px solid transparent;
      border-top: 5px solid white;
    }
    ^userName {
      color: /*%GREY4%*/;
      font-weight: 600;
      font-size: 12px;
    }
    ^agentName{
      color: /*%GREY3%*/;
      font-weight: 400;
      font-size: 11px;
    }
    ^ .name-container {
      max-width: 75px;
      white-space: nowrap;
      overflow: hidden;
      text-overflow: ellipsis;
    }
    ^container {
      cursor: pointer;
      display: flex;
      align-items: center;
      width: 100%;
      height: 100%;
    }
  `,

  methods: [
    function initE() {
      this
        .addClass(this.myClass())
        .start().addClass(this.myClass('container'))
          .on('click', () => {
            this.tag(this.SubMenuView.create({
              menu: this.Menu.create({ id: this.theme.settingsRootMenu })
            }));
          })
          .start()
             .add(this.slot((subject$user) => {
              if ( ! this.subject.user ) return;
              return this.E().addClass('name-container')
                  .start('span').addClass(this.myClass('userName'))
                    .add(this.subject.user.toSummary())
                  .end();
            }))
            .add(this.slot((subject$realUser) => {
              if ( ! this.subject.realUser ) return;
              return this.E().addClass('name-container')
                  .start('span').addClass(this.myClass('agentName'))
                    .add( this.subject.realUser.toSummary() )
                  .end();
            }))
          .end()
          .start()
            .addClass(this.myClass('carrot'))
          .end()
        .end();
    }
  ]
});
<|endoftext|>