<commit_before>// @flow

import map from 'lodash/map';
import groupBy from 'lodash/groupBy';
import getCollectionProp from './getCollectionProp';

/**
* It returns grouped results
* @param  {string} prop    grouping result by this object key
* @param  {function} [labelIteratee = (records, field) => field]
* group field value format method. Default return original value
* @param  {function} [valueIteratee = (records, field) => records.length]
* group field items method. Default is count group elements
* @return {object}          {'group1': valueIteratee return, 'group2': valueIteratee return}
*
* @example
* Grouping by date, where the value sum every record person property
* [{date: '2016-02-03', person: '3'}...]
* // => {'2016-02-03': 22, '2016-02-04': 25 }
* collectionGroupBy(data, 'date', (value, key) => (sum(map(value, i => parseInt(i.person)))))
*/
export const collectionGroupBy = (
  data: Array<{}>,
  prop: string,
  labelIteratee?: (groupRecords: Array<{}>, groupProp: string) => string
    = (groupRecords, groupProp) => groupProp,
  valueIteratee?: (groupRecords: Array<{}>, groupProp: string) => number
    = groupRecords => groupRecords.length,
): Array<{ id: string, title: number }> =>
{
  return map(
    groupBy(data, x => x[prop]),
    (groupRecords, groupProp) =>
    ({
      id: labelIteratee(groupRecords, groupProp),
      title: valueIteratee(groupRecords, groupProp),
    }),
  );
};

/**
 * Collect collection and count data
 *
 * @since 3.9.0
 * @static
 * @memberof array
 * @param  {array} data collection
 * @param  {string} prop field of record within collection
 * @param  {func} method accumulator (ex. sum, mean, count ...)
 * @param  {string|array} group multidimension results
 * @return {object}
 * @example
 * pivotTable(data, 'gender', count),
 * // => 12
 *
 * pivotTable(data, 'gender', values => values.length),
 * // => 12
 *
 * pivotTable(data, 'gender', countUnique),
 * // => 2
 *
 * // collectionGroupBy(data, 'gender'),
 * // => [ { id: '1', title: 8 }, { id: '2', title: 4 } ]
 *
 * pivotTable(data, 'gender', count, 'gender'),
 * // => [ { id: '1', title: 8 }, { id: '2', title: 4 }, { id: 'count', title: 12 } ]
 *
 * pivotTable(data, 'age', mean, 'visits'),
 * // => { id: '2017-07-23', title: 22 }, { id: '2017-07-22', title: 28.666666666666668 } ...,
 *
 * pivotTable(data, 'age', mean, ['visits', 'gender']),
 * // => { id: '2017-07-23', title: [ { id: '1', title: 8 }, { id: '2', title: 4 }, { id: 'mean', title: 12 } ] }, ...
 *
 * pivotTable(data, mean),
 * // => { id: 6.5, name: NaN, gender: 1.3333333333333333, age: 26.5, visits: NaN }
 *
 */
const pivotTable2 = function (data: Array<{}>, prop?: string|Function, method?: Function, group?: string)
{
  if (typeof prop === 'undefined' || Array.isArray(data) === false)
  {
    return null;
  }

  let result;

  if (typeof prop === 'string' && typeof method === 'function')
  {
    let results = [];

    if (group)
    {
      const groups = Array.isArray(group) ? group : [group];
      let groupsIndex = 0;

      const groupDataIterator = (groupData) =>
      {
        groupsIndex++;

        const groupResult = method(getCollectionProp(groupData, prop));

        let child;

        if (groupsIndex < groups.length)
        {
          child = collectionGroupBy(groupData, groups[groupsIndex], undefined, groupDataIterator);
        }

        groupsIndex -= 1;

        if (child)
        {
          child.push({
            id: method.name || 'result',
            title: groupResult,
          });

          return child;
        }

        return groupResult;
      }

      /**
       * Collect data by group props. Iterate groups and call method every group
       * @type {array} [{ id: 'male', title: 2 }, { id: 'male', title: 2 }]
       * id: group field value,
       * title: calculated method results (ex. count, sum, mean ...)
       */
      results = collectionGroupBy(
        data,
        groups[0],
        undefined,
        groupDataIterator,
      );
    }

    /**
     * @example
     * // => [1,2,1,1,1,2,1]
     */
    const values = getCollectionProp(data, prop);

    results.push({
      id: method.name || 'result',
      title: values.length ? method(values) : 0,
    });

    result = (results.length === 1) ? results[0].title : results;
  }
  else if (typeof prop === 'function')
  {
    const method = prop;
    const columns = Object.keys(data[0]);

    result = columns.reduce(
      (results, column) => ({
        ...results,
        [column]: pivotTable(data, column, method),
      }),
      {},
    );
  }

  // if (this instanceof pivotTable)
  if (this)
  {
    this.result = result;
  }

  return result;
}

const pivotTable = function (data: Array<{}>, prop?: string|Function, method?: Function, group?: string|Array<string>)
{
  if (typeof prop === 'undefined' || Array.isArray(data) === false)
  {
    return null;
  }

  let result;

  if (typeof prop === 'string' && typeof method === 'function')
  {
    let results = [];

    if (group)
    {
      const groups = Array.isArray(group) ? group : [group];
      let groupsIndex = 0;

      const groupDataIterator = (groupData) =>
      {
        groupsIndex++;

        const groupResult = method(getCollectionProp(groupData, prop));

        let child;

        if (groupsIndex < groups.length)
        {
          child = collectionGroupBy(groupData, groups[groupsIndex], undefined, groupDataIterator);
        }

        groupsIndex--;

        if (child)
        {
          child.push({
            id: method.name || 'result',
            title: groupResult,
          });

          return child;
        }

        return groupResult;
      }

      /**
       * Collect data by group props. Iterate groups and call method every group
       * @type {array} [{ id: 'male', title: 2 }, { id: 'male', title: 2 }]
       * id: group field value,
       * title: calculated method results (ex. count, sum, mean ...)
       */
      results = collectionGroupBy(
        data,
        groups[0],
        undefined,
        groupDataIterator,
      );
    }

    /**
     * @example
     * // => [1,2,1,1,1,2,1]
     */
    const values = getCollectionProp(data, prop);

    results.push({
      id: method.name || 'result',
      title: values.length ? method(values) : 0,
    });

    result = (results.length === 1) ? results[0].title : results;
  }
  else if (typeof prop === 'function')
  {
    const method = prop;
    const columns = Object.keys(data[0]);

    result = columns.reduce(
      (results, column) => ({
        ...results,
        [column]: pivotTable(data, column, method),
      }),
      {},
    );
  }

  if (this instanceof pivotTable)
  {
    this.result = result;
    return;
  }

   return result;
}

pivotTable.prototype =
{
  /**
   * Convert pivotTable data to collection
   * @example
   * { id: '2017-07-23', title: [ { id: 'male', title: 8 }, { id: 'female', title: 4 }, { id: 'count', title: 12 } ] }
   * // => [{ id, male, femail, count }, ... ]
   */
  toArray: function(transform)
  {
    if (!this.result)
    {
      return [];
    }

    return this.result.map(({ id, title }) =>
    {
      const record = {id};

      if (Array.isArray(title))
      {
        title.forEach(child => record[child.id] = child.title);
      }
      else
      {
        record[id] = title;
      }

      return typeof transform === 'function' ? transform(record) : record;
    });
  },
};

export default pivotTable;
<commit_msg>fix(array): pivotTable<commit_after>// @flow

import map from 'lodash/map';
import groupBy from 'lodash/groupBy';
import getCollectionProp from './getCollectionProp';

/**
* It returns grouped results
* @param  {string} prop    grouping result by this object key
* @param  {function} [labelIteratee = (records, field) => field]
* group field value format method. Default return original value
* @param  {function} [valueIteratee = (records, field) => records.length]
* group field items method. Default is count group elements
* @return {object}          {'group1': valueIteratee return, 'group2': valueIteratee return}
*
* @example
* Grouping by date, where the value sum every record person property
* [{date: '2016-02-03', person: '3'}...]
* // => {'2016-02-03': 22, '2016-02-04': 25 }
* collectionGroupBy(data, 'date', (value, key) => (sum(map(value, i => parseInt(i.person)))))
*/
export const collectionGroupBy = (
  data: Array<{}>,
  prop: string,
  labelIteratee?: (groupRecords: Array<{}>, groupProp: string) => string
    = (groupRecords, groupProp) => groupProp,
  valueIteratee?: (groupRecords: Array<{}>, groupProp: string) => number
    = groupRecords => groupRecords.length,
): Array<{ id: string, title: number }> =>
{
  return map(
    groupBy(data, x => x[prop]),
    (groupRecords, groupProp) =>
    ({
      id: labelIteratee(groupRecords, groupProp),
      title: valueIteratee(groupRecords, groupProp),
    }),
  );
};

/**
 * Collect collection and count data
 *
 * @since 3.9.0
 * @static
 * @memberof array
 * @param  {array} data collection
 * @param  {string} prop field of record within collection
 * @param  {func} method accumulator (ex. sum, mean, count ...)
 * @param  {string|array} group multidimension results
 * @return {object}
 * @example
 * pivotTable(data, 'gender', count),
 * // => 12
 *
 * pivotTable(data, 'gender', values => values.length),
 * // => 12
 *
 * pivotTable(data, 'gender', countUnique),
 * // => 2
 *
 * // collectionGroupBy(data, 'gender'),
 * // => [ { id: '1', title: 8 }, { id: '2', title: 4 } ]
 *
 * pivotTable(data, 'gender', count, 'gender'),
 * // => [ { id: '1', title: 8 }, { id: '2', title: 4 }, { id: 'count', title: 12 } ]
 *
 * pivotTable(data, 'age', mean, 'visits'),
 * // => { id: '2017-07-23', title: 22 }, { id: '2017-07-22', title: 28.666666666666668 } ...,
 *
 * pivotTable(data, 'age', mean, ['visits', 'gender']),
 * // => { id: '2017-07-23', title: [ { id: '1', title: 8 }, { id: '2', title: 4 }, { id: 'mean', title: 12 } ] }, ...
 *
 * pivotTable(data, mean),
 * // => { id: 6.5, name: NaN, gender: 1.3333333333333333, age: 26.5, visits: NaN }
 *
 */
const pivotTable2 = function (data: Array<{}>, prop?: string|Function, method?: Function, group?: string)
{
  if (typeof prop === 'undefined' || Array.isArray(data) === false)
  {
    return null;
  }

  let result;

  if (typeof prop === 'string' && typeof method === 'function')
  {
    let results = [];

    if (group)
    {
      const groups = Array.isArray(group) ? group : [group];
      let groupsIndex = 0;

      const groupDataIterator = (groupData) =>
      {
        groupsIndex++;

        const groupResult = method(getCollectionProp(groupData, prop));

        let child;

        if (groupsIndex < groups.length)
        {
          child = collectionGroupBy(groupData, groups[groupsIndex], undefined, groupDataIterator);
        }

        groupsIndex -= 1;

        if (child)
        {
          child.push({
            id: '_result',
            title: groupResult,
          });

          return child;
        }

        return groupResult;
      }

      /**
       * Collect data by group props. Iterate groups and call method every group
       * @type {array} [{ id: 'male', title: 2 }, { id: 'male', title: 2 }]
       * id: group field value,
       * title: calculated method results (ex. count, sum, mean ...)
       */
      results = collectionGroupBy(
        data,
        groups[0],
        undefined,
        groupDataIterator,
      );
    }

    /**
     * @example
     * // => [1,2,1,1,1,2,1]
     */
    const values = getCollectionProp(data, prop);

    results.push({
      id: method.name || 'result',
      title: values.length ? method(values) : 0,
    });

    result = (results.length === 1) ? results[0].title : results;
  }
  else if (typeof prop === 'function')
  {
    const method = prop;
    const columns = Object.keys(data[0]);

    result = columns.reduce(
      (results, column) => ({
        ...results,
        [column]: pivotTable(data, column, method),
      }),
      {},
    );
  }

  // if (this instanceof pivotTable)
  if (this)
  {
    this.result = result;
  }

  return result;
}

const pivotTable = function (data: Array<{}>, prop?: string|Function, method?: Function, group?: string|Array<string>)
{
  if (typeof prop === 'undefined' || Array.isArray(data) === false)
  {
    return null;
  }

  let result;

  if (typeof prop === 'string' && typeof method === 'function')
  {
    let results = [];

    if (group)
    {
      const groups = Array.isArray(group) ? group : [group];
      let groupsIndex = 0;

      const groupDataIterator = (groupData) =>
      {
        groupsIndex++;

        const groupResult = method(getCollectionProp(groupData, prop));

        let child;

        if (groupsIndex < groups.length)
        {
          child = collectionGroupBy(groupData, groups[groupsIndex], undefined, groupDataIterator);
        }

        groupsIndex--;

        if (child)
        {
          child.push({
            id: method.name || 'result',
            title: groupResult,
          });

          return child;
        }

        return groupResult;
      }

      /**
       * Collect data by group props. Iterate groups and call method every group
       * @type {array} [{ id: 'male', title: 2 }, { id: 'male', title: 2 }]
       * id: group field value,
       * title: calculated method results (ex. count, sum, mean ...)
       */
      results = collectionGroupBy(
        data,
        groups[0],
        undefined,
        groupDataIterator,
      );
    }

    /**
     * @example
     * // => [1,2,1,1,1,2,1]
     */
    const values = getCollectionProp(data, prop);

    results.push({
      id: method.name || 'result',
      title: values.length ? method(values) : 0,
    });

    result = (results.length === 1) ? results[0].title : results;
  }
  else if (typeof prop === 'function')
  {
    const method = prop;
    const columns = Object.keys(data[0]);

    result = columns.reduce(
      (results, column) => ({
        ...results,
        [column]: pivotTable(data, column, method),
      }),
      {},
    );
  }

  if (this instanceof pivotTable)
  {
    this.result = result;
    return;
  }

   return result;
}

pivotTable.prototype =
{
  /**
   * Convert pivotTable data to collection
   * @example
   * { id: '2017-07-23', title: [ { id: 'male', title: 8 }, { id: 'female', title: 4 }, { id: 'count', title: 12 } ] }
   * // => [{ id, male, femail, count }, ... ]
   */
  toArray: function(transform)
  {
    if (!this.result)
    {
      return [];
    }

    return this.result.map(({ id, title }) =>
    {
      const record = {id};

      if (Array.isArray(title))
      {
        title.forEach(child => record[child.id] = child.title);
      }
      else
      {
        record[id] = title;
      }

      return typeof transform === 'function' ? transform(record) : record;
    });
  },
};

export default pivotTable;
<|endoftext|>


<commit_before>
process.env.NODE_ENV = 'test'

var assert = require('assert')
var connect = require('connect')
var request = require('supertest')
var session = require('..')

describe('Cookie Session', function () {
  var cookie

  describe('when options.name = my.session', function () {
    it('should use my.session for cookie name', function (done) {
      var app = App({ name: 'my.session' })
      app.use(function (req, res, next) {
        req.session.message = 'hi'
        res.end()
      })

      request(app)
      .get('/')
      .expect(shouldHaveCookie('my.session'))
      .expect(200, done)
    })
  })

  describe('when options.signed = true', function () {
    describe('when app.keys are set', function () {
      it('should work', function (done) {
        var app = connect()
        app.use(session({
          keys: ['a', 'b']
        }))
        app.use(function (req, res, next) {
          req.session.message = 'hi'
          res.end()
        })

        request(app)
        .get('/')
        .expect(200, '', done)
      })
    })

    describe('when app.keys are not set', function () {
      it('should throw', function () {
        assert.throws(function () {
          session()
        })
      })
    })
  })

  describe('when options.signed = false', function () {
    describe('when app.keys are not set', function () {
      it('should work', function (done) {
        var app = connect()
        app.use(session({
          signed: false
        }))
        app.use(function (req, res, next) {
          req.session.message = 'hi'
          res.end()
        })

        request(app)
        .get('/')
        .expect(200, done)
      })
    })
  })

  describe('when options.secure = true', function () {
    describe('when connection not secured', function () {
      it('should not Set-Cookie', function (done) {
        var app = App({ secure: true })
        app.use(function (req, res, next) {
          process.nextTick(function () {
            req.session.message = 'hello!'
            res.end('greetings')
          })
        })

        request(app)
        .get('/')
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })
  })

  describe('when the session contains a ;', function () {
    it('should still work', function (done) {
      var app = App()
      app.use(function (req, res, next) {
        if (req.method === 'POST') {
          req.session.string = ';'
          res.statusCode = 204
          res.end()
        } else {
          res.end(req.session.string)
        }
      })

      request(app)
      .post('/')
      .expect(shouldHaveCookie('session'))
      .expect(204, function (err, res) {
        if (err) return done(err)
        var cookie = res.headers['set-cookie']
        request(app)
        .get('/')
        .set('Cookie', cookie.join(';'))
        .expect(';', done)
      })
    })
  })

  describe('when the session is invalid', function () {
    it('should create new session', function (done) {
      var app = App({ name: 'my.session', signed: false })
      app.use(function (req, res, next) {
        res.end(String(req.session.isNew))
      })

      request(app)
      .get('/')
      .set('Cookie', 'my.session=bogus')
      .expect(200, 'true', done)
    })
  })

  describe('new session', function () {
    describe('when not accessed', function () {
      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          res.end('greetings')
        })

        request(app)
        .get('/')
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })

    describe('when accessed and not populated', function (done) {
      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session
          res.end('greetings')
        })

        request(app)
        .get('/')
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })

    describe('when populated', function (done) {
      it('should Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session.message = 'hello'
          res.end()
        })

        request(app)
        .get('/')
        .expect(shouldHaveCookie('session'))
        .expect(200, function (err, res) {
          if (err) return done(err)
          cookie = res.header['set-cookie'].join(';')
          done()
        })
      })

      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          res.end(JSON.stringify(this.session))
        })

        request(app)
        .get('/')
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })
  })

  describe('saved session', function () {
    describe('when not accessed', function () {
      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          res.end('aklsjdfklasjdf')
        })

        request(app)
        .get('/')
        .set('Cookie', cookie)
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })

    describe('when accessed but not changed', function () {
      it('should be the same session', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          assert.equal(req.session.message, 'hello')
          res.end('aklsjdfkljasdf')
        })

        request(app)
        .get('/')
        .set('Cookie', cookie)
        .expect(200, done)
      })

      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          assert.equal(req.session.message, 'hello')
          res.end('aklsjdfkljasdf')
        })

        request(app)
        .get('/')
        .set('Cookie', cookie)
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })

    describe('when accessed and changed', function () {
      it('should Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session.money = '$$$'
          res.end('klajsdlkfjadsf')
        })

        request(app)
        .get('/')
        .set('Cookie', cookie)
        .expect(shouldHaveCookie('session'))
        .expect(200, done)
      })
    })
  })

  describe('when session = ', function () {
    describe('null', function () {
      it('should expire the session', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session = null
          res.end('lkajsdf')
        })

        request(app)
        .get('/')
        .expect(shouldHaveCookie('session'))
        .expect(200, done)
      })

      it('should no longer return a session', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session = null
          res.end(JSON.stringify(req.session))
        })

        request(app)
        .get('/')
        .expect(shouldHaveCookie('session'))
        .expect(200, 'null', done)
      })
    })

    describe('{}', function () {
      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session = {}
          res.end('hello, world')
        })

        request(app)
        .get('/')
        .expect(shouldNotSetCookies())
        .expect(200, 'hello, world', done)
      })
    })

    describe('{a: b}', function () {
      it('should create a session', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session = { message: 'hello' }
          res.end('klajsdfasdf')
        })

        request(app)
        .get('/')
        .expect(shouldHaveCookie('session'))
        .expect(200, done)
      })
    })

    describe('anything else', function () {
      it('should throw', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session = 'aklsdjfasdf'
        })

        request(app)
        .get('/')
        .expect(500, done)
      })
    })
  })

  describe('req.session', function () {
    describe('.isPopulated', function () {
      it('should be false on new session', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          res.end(String(req.session.isPopulated))
        })

        request(app)
        .get('/')
        .expect(200, 'false', done)
      })

      it('should be true after adding property', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session.message = 'hello!'
          res.end(String(req.session.isPopulated))
        })

        request(app)
        .get('/')
        .expect(200, 'true', done)
      })
    })
  })

  describe('req.sessionOptions', function () {
    it('should be the session options', function (done) {
      var app = App({ name: 'my.session' })
      app.use(function (req, res, next) {
        res.end(String(req.sessionOptions.name))
      })

      request(app)
      .get('/')
      .expect(200, 'my.session', done)
    })

    it('should alter the cookie setting', function (done) {
      var app = App({ maxAge: 3600000, name: 'my.session' })
      app.use(function (req, res, next) {
        if (req.url === '/max') {
          req.sessionOptions.maxAge = 6500000
        }

        req.session.message = 'hello!'
        res.end()
      })

      request(app)
      .get('/')
      .expect(function (res) {
        var date = new Date(res.headers.date)
        var expires = new Date(res.headers['set-cookie'][0].match(/expires=([^;]+)/)[1])
        assert.ok(expires - date <= 3600000)
      })
      .expect(200, function (err) {
        if (err) return done(err)
        request(app)
        .get('/max')
        .expect(function (res) {
          var date = new Date(res.headers.date)
          var expires = new Date(res.headers['set-cookie'][0].match(/expires=([^;]+)/)[1])
          assert.ok(expires - date > 5000000)
        })
        .expect(200, done)
      })
    })
  })
})

function App (options) {
  options = options || {}
  options.keys = ['a', 'b']
  var app = connect()
  app.use(session(options))
  return app
}

function shouldHaveCookie (name) {
  return function (res) {
    assert.ok(res.headers['set-cookie'].some(function (cookie) {
      return cookie.split('=')[0] === name
    }), 'should have cookie "' + name + '"')
  }
}

function shouldNotSetCookies () {
  return function (res) {
    assert.strictEqual(res.headers['set-cookie'], undefined, 'should not set cookies')
  }
}
<commit_msg>tests: fix some test descriptions<commit_after>
process.env.NODE_ENV = 'test'

var assert = require('assert')
var connect = require('connect')
var request = require('supertest')
var session = require('..')

describe('Cookie Session', function () {
  var cookie

  describe('when options.name = my.session', function () {
    it('should use my.session for cookie name', function (done) {
      var app = App({ name: 'my.session' })
      app.use(function (req, res, next) {
        req.session.message = 'hi'
        res.end()
      })

      request(app)
      .get('/')
      .expect(shouldHaveCookie('my.session'))
      .expect(200, done)
    })
  })

  describe('when options.signed = true', function () {
    describe('when options.keys are set', function () {
      it('should work', function (done) {
        var app = connect()
        app.use(session({
          keys: ['a', 'b']
        }))
        app.use(function (req, res, next) {
          req.session.message = 'hi'
          res.end()
        })

        request(app)
        .get('/')
        .expect(200, '', done)
      })
    })

    describe('when options.keys are not set', function () {
      it('should throw', function () {
        assert.throws(function () {
          session()
        })
      })
    })
  })

  describe('when options.signed = false', function () {
    describe('when app.keys are not set', function () {
      it('should work', function (done) {
        var app = connect()
        app.use(session({
          signed: false
        }))
        app.use(function (req, res, next) {
          req.session.message = 'hi'
          res.end()
        })

        request(app)
        .get('/')
        .expect(200, done)
      })
    })
  })

  describe('when options.secure = true', function () {
    describe('when connection not secured', function () {
      it('should not Set-Cookie', function (done) {
        var app = App({ secure: true })
        app.use(function (req, res, next) {
          process.nextTick(function () {
            req.session.message = 'hello!'
            res.end('greetings')
          })
        })

        request(app)
        .get('/')
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })
  })

  describe('when the session contains a ;', function () {
    it('should still work', function (done) {
      var app = App()
      app.use(function (req, res, next) {
        if (req.method === 'POST') {
          req.session.string = ';'
          res.statusCode = 204
          res.end()
        } else {
          res.end(req.session.string)
        }
      })

      request(app)
      .post('/')
      .expect(shouldHaveCookie('session'))
      .expect(204, function (err, res) {
        if (err) return done(err)
        var cookie = res.headers['set-cookie']
        request(app)
        .get('/')
        .set('Cookie', cookie.join(';'))
        .expect(';', done)
      })
    })
  })

  describe('when the session is invalid', function () {
    it('should create new session', function (done) {
      var app = App({ name: 'my.session', signed: false })
      app.use(function (req, res, next) {
        res.end(String(req.session.isNew))
      })

      request(app)
      .get('/')
      .set('Cookie', 'my.session=bogus')
      .expect(200, 'true', done)
    })
  })

  describe('new session', function () {
    describe('when not accessed', function () {
      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          res.end('greetings')
        })

        request(app)
        .get('/')
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })

    describe('when accessed and not populated', function (done) {
      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session
          res.end('greetings')
        })

        request(app)
        .get('/')
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })

    describe('when populated', function (done) {
      it('should Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session.message = 'hello'
          res.end()
        })

        request(app)
        .get('/')
        .expect(shouldHaveCookie('session'))
        .expect(200, function (err, res) {
          if (err) return done(err)
          cookie = res.header['set-cookie'].join(';')
          done()
        })
      })

      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          res.end(JSON.stringify(this.session))
        })

        request(app)
        .get('/')
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })
  })

  describe('saved session', function () {
    describe('when not accessed', function () {
      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          res.end('aklsjdfklasjdf')
        })

        request(app)
        .get('/')
        .set('Cookie', cookie)
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })

    describe('when accessed but not changed', function () {
      it('should be the same session', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          assert.equal(req.session.message, 'hello')
          res.end('aklsjdfkljasdf')
        })

        request(app)
        .get('/')
        .set('Cookie', cookie)
        .expect(200, done)
      })

      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          assert.equal(req.session.message, 'hello')
          res.end('aklsjdfkljasdf')
        })

        request(app)
        .get('/')
        .set('Cookie', cookie)
        .expect(shouldNotSetCookies())
        .expect(200, done)
      })
    })

    describe('when accessed and changed', function () {
      it('should Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session.money = '$$$'
          res.end('klajsdlkfjadsf')
        })

        request(app)
        .get('/')
        .set('Cookie', cookie)
        .expect(shouldHaveCookie('session'))
        .expect(200, done)
      })
    })
  })

  describe('when session = ', function () {
    describe('null', function () {
      it('should expire the session', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session = null
          res.end('lkajsdf')
        })

        request(app)
        .get('/')
        .expect(shouldHaveCookie('session'))
        .expect(200, done)
      })

      it('should no longer return a session', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session = null
          res.end(JSON.stringify(req.session))
        })

        request(app)
        .get('/')
        .expect(shouldHaveCookie('session'))
        .expect(200, 'null', done)
      })
    })

    describe('{}', function () {
      it('should not Set-Cookie', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session = {}
          res.end('hello, world')
        })

        request(app)
        .get('/')
        .expect(shouldNotSetCookies())
        .expect(200, 'hello, world', done)
      })
    })

    describe('{a: b}', function () {
      it('should create a session', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session = { message: 'hello' }
          res.end('klajsdfasdf')
        })

        request(app)
        .get('/')
        .expect(shouldHaveCookie('session'))
        .expect(200, done)
      })
    })

    describe('anything else', function () {
      it('should throw', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session = 'aklsdjfasdf'
        })

        request(app)
        .get('/')
        .expect(500, done)
      })
    })
  })

  describe('req.session', function () {
    describe('.isPopulated', function () {
      it('should be false on new session', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          res.end(String(req.session.isPopulated))
        })

        request(app)
        .get('/')
        .expect(200, 'false', done)
      })

      it('should be true after adding property', function (done) {
        var app = App()
        app.use(function (req, res, next) {
          req.session.message = 'hello!'
          res.end(String(req.session.isPopulated))
        })

        request(app)
        .get('/')
        .expect(200, 'true', done)
      })
    })
  })

  describe('req.sessionOptions', function () {
    it('should be the session options', function (done) {
      var app = App({ name: 'my.session' })
      app.use(function (req, res, next) {
        res.end(String(req.sessionOptions.name))
      })

      request(app)
      .get('/')
      .expect(200, 'my.session', done)
    })

    it('should alter the cookie setting', function (done) {
      var app = App({ maxAge: 3600000, name: 'my.session' })
      app.use(function (req, res, next) {
        if (req.url === '/max') {
          req.sessionOptions.maxAge = 6500000
        }

        req.session.message = 'hello!'
        res.end()
      })

      request(app)
      .get('/')
      .expect(function (res) {
        var date = new Date(res.headers.date)
        var expires = new Date(res.headers['set-cookie'][0].match(/expires=([^;]+)/)[1])
        assert.ok(expires - date <= 3600000)
      })
      .expect(200, function (err) {
        if (err) return done(err)
        request(app)
        .get('/max')
        .expect(function (res) {
          var date = new Date(res.headers.date)
          var expires = new Date(res.headers['set-cookie'][0].match(/expires=([^;]+)/)[1])
          assert.ok(expires - date > 5000000)
        })
        .expect(200, done)
      })
    })
  })
})

function App (options) {
  options = options || {}
  options.keys = ['a', 'b']
  var app = connect()
  app.use(session(options))
  return app
}

function shouldHaveCookie (name) {
  return function (res) {
    assert.ok(res.headers['set-cookie'].some(function (cookie) {
      return cookie.split('=')[0] === name
    }), 'should have cookie "' + name + '"')
  }
}

function shouldNotSetCookies () {
  return function (res) {
    assert.strictEqual(res.headers['set-cookie'], undefined, 'should not set cookies')
  }
}
<|endoftext|>


<commit_before>// imports
var _ = require('lodash');
// gruntfile
module.exports = function (grunt) {
    'use strict';

        // development folders and files
    var SOURCE_PATH             =   'src/',
        SASS_PATH               =   SOURCE_PATH + 'styles/',
        JAVASCRIPT_PATH         =   SOURCE_PATH + 'scripts/',
        TEMPLATES_PATH          =   SOURCE_PATH + 'templates/',
        TEMPLATES_LAYOUTS_PATH  =   TEMPLATES_PATH + 'layouts/',
        TEMPLATES_PAGES_PATH    =   TEMPLATES_PATH + 'pages/',
        TEMPLATES_PARTIALS_PATH =   TEMPLATES_PATH + 'parts/',
        TEMPLATES_DATA_PATH     =   TEMPLATES_PATH + 'data/',
        HTML5_BOILERPLATE_PATH  =   'lib/html5-boilerplate/',

        SASS_FILES              =   [SASS_PATH + '**/*.scss'],
        JAVASCRIPT_SOURCES      =   ['*.js', JAVASCRIPT_PATH + '**/*.js'],
        HTACCESS_BASE_FILE      =   HTML5_BOILERPLATE_PATH + '.htaccess',
        HTACCESS_FILE           =   SOURCE_PATH + 'htaccess.conf',
        MANIFEST_WEBAPP_NAME    =   'manifest.webapp',

        // build generated output
        BUILD_PATH              =   'build/',
        HTDOCS_PATH             =   BUILD_PATH + 'www/',
        CSS_PATH                =   HTDOCS_PATH + 'css/',
        FONTS_PATH              =   HTDOCS_PATH + 'fonts/',
        JS_PATH                 =   HTDOCS_PATH + 'js/',
        CSS_FILES               =   [CSS_PATH + '**/*.css'],
        HTML_FILES              =   [HTDOCS_PATH + '**/*.html'],
        JS_FILES                =   [JS_PATH + '**/*.js'],
        FILES_TO_CACHE          =   HTML_FILES.concat(CSS_FILES, JS_FILES),

        //themes
        UBUNTU_THEME_PATH       =   'lib/ubuntu-html5-theme/0.1/ambiance/',

        // browser compatibility
        BROWSER_SUPPORT = [
            'last 2 version',
            'ff >= 18'
        ],

        // linters
        JSLINT_GLOBALS = [
            'module',
            'require',
            'console'
        ],
        JSLINT_DIRECTIVES = {
            nomen: true,
            predef: JSLINT_GLOBALS
        },
        GJSLINT_DISABLE = [
            '1',  //Extra space after "function" (Error 1)
            '6',  //Wrong indentation: expected x but got y (Error 6)
            '220' //No docs found for member (Error 220)
        ],
        GJSLINT_OPTIONS = {
            flags: [
                '--disable ' + GJSLINT_DISABLE.join(','),
                '--strict'
            ],
            reporter: {
                name: 'console'
            }
        },
        JS_LINTER = 'jslint';

// Grunt tasks
// ---------------------------------------------------------------------------
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        // Stylesheets
        compass: {
            options: {
                sassDir: SASS_PATH,
                cssDir: CSS_PATH,
                bundleExec: true,
                relativeAssets: true,
                fontsDir: FONTS_PATH
            },
            dev: {
                options: {
                    environment: 'development'
                }
            }
        },
        autoprefixer: {
            all: {
                options: {
                    browsers: BROWSER_SUPPORT
                },
                files: [{
                    expand: true,
                    cwd: CSS_PATH,
                    src: '**/*.css',
                    dest: CSS_PATH
                }]
            }
        },

        // Javascript
        jsvalidate: {
            all: JAVASCRIPT_SOURCES
        },
        jshint: {
            all: {
                src: JAVASCRIPT_SOURCES
            }
        },
        jslint: {
            all: {
                src: JAVASCRIPT_SOURCES,
                directives: JSLINT_DIRECTIVES
            }
        },

        gjslint: {
            options: GJSLINT_OPTIONS,
            all: {
                src: JAVASCRIPT_SOURCES
            }
        },

        // Apache Config
        concat: {
            htaccess: {
                files: [{
                    src: [
                        HTACCESS_BASE_FILE,
                        HTACCESS_FILE
                    ],
                    dest: HTDOCS_PATH + '.htaccess'
                }]
            }
        },

        // Templates
        assemble: {
            options: {
                flatten: true,
                layoutdir: TEMPLATES_LAYOUTS_PATH,
                partials: [TEMPLATES_PARTIALS_PATH + '**/*.hbs'],
                data: [TEMPLATES_DATA_PATH + '**/*.{json,yml}'],
                pkg: '<%= pkg %>'
            },
            pages: {
                files: [{
                    src: [
                        TEMPLATES_PAGES_PATH + '/**/*.hbs'
                    ],
                    dest: HTDOCS_PATH
                }]
            }
        },

        // Open Web App Manifest
        openwebapp: {
            options: {
                name: '<%= pkg.fullName %>',
                version: '<%= pkg.version %>',
                description: '<%= pkg.description %>',
                launch_path: '/app.html',
                developer: {
                    name: '<%= pkg.author.name %>',
                    url: '<%= pkg.author.url %>'
                },
                // icons: {
                //     "128": "/img/icons/app_128.png"
                // },
                installs_allowed_from: ['*']
            },
            all: {
                dest: HTDOCS_PATH + MANIFEST_WEBAPP_NAME
            }
        },

        //HTML indentation for generated pages
        prettify: {
            options: {
                indent: 4,
                indent_char: ' ',
                wrap_line_length: 78,
                brace_style: 'expand',
                unformatted: ['a', 'sub', 'sup', 'b', 'i', 'u']
            },
            all: {
                expand: true,
                cwd: HTDOCS_PATH,
                ext: '.html',
                src: ['**/*.html'],
                dest: HTDOCS_PATH
            }
        },

        //HTML Validation
        validation: {
            options: {
                reportpath: '.temp/validation-report.json',
                path: '.temp/validation-staus.json'
            },
            all: {
                options: {
                    reset: true
                },
                files: [{
                    src: HTML_FILES
                }]
            },
            repeat: {
                files: [{
                    src: HTML_FILES
                }]
            }
        },

        // Theme assets
        copy: {
            ubuntu_fonts: {
                files: [{
                    expand: true,
                    src: [UBUNTU_THEME_PATH + 'fonts/**/*.{ttf,woff,svg,eot}'],
                    dest: HTDOCS_PATH + 'fonts/ubuntu/',
                    flatten: true
                }]
            },
            ubuntu_css: {
                files: [{
                    expand: true,
                    src: [UBUNTU_THEME_PATH + 'css/**/*.css'],
                    dest: HTDOCS_PATH + 'css/ubuntu/',
                    flatten: true
                }]
            }
        },

        // Appcache
        manifest: {
            generate: {
                options: {
                    network: [
                    ],
                    process: function (path) {
                        // remove documentRoot part of the cached files paths
                        return path.substring(HTDOCS_PATH.length);
                    },
                    hash: true,
                    timestamp: false,
                    basePath: ''
                },
                src: FILES_TO_CACHE,
                dest: HTDOCS_PATH + 'manifest.appcache'
            }
        },

        // Watch
        watch: {
            options: {
                spawn: false
            },
            javascript: {
                files: JAVASCRIPT_SOURCES,
                tasks: ['jsvalidate', JS_LINTER]
            },
            css: {
                files: CSS_FILES,
                tasks: ['autoprefixer'],
                options: {
                    spawn: true
                }
            },
            html: {
                files: HTML_FILES,
                tasks: ['prettify', 'validation:repeat'],
                options: {
                    spawn: true
                }
            },
            assemble: {
                files: [
                    TEMPLATES_PATH + '/**/*.hbs',
                    TEMPLATES_DATA_PATH + '**/*.{json,yml}'
                ],
                tasks: ['assemble'],
                options: {
                    spawn: true
                }
            },
            update_htaccess: {
                files: HTACCESS_FILE,
                tasks: ['concat:htaccess']
            },
            sass: {
                files: SASS_FILES,
                tasks: ['compass'],
                options: {
                    spawn: true
                }
            },
            cache: {
                files: FILES_TO_CACHE,
                tasks: ['manifest']
            }
        }
    });

    // on watch events configure certain tasks to only run on changed file
    grunt.event.on('watch', function (action, filepath) {
        if (action === 'deleted') { return; }
        grunt.config(['jsvalidate', 'all'], filepath);
        grunt.config(['jshint', 'all'], filepath);
        grunt.config(['jslint', 'all'], filepath);
        grunt.config(['gjslint', 'all'], filepath);
        grunt.config(['autoprefixer', 'all'], filepath);
    });

    //run grunt.loadNpmTasks on each grunt plugin found in your package.json
    require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
    grunt.loadNpmTasks('assemble');

    grunt.registerMultiTask(
        'openwebapp',
        'generate manifest.webapp file',
        function () {
            var task_options = {},
                target_options,
                done = this.async();
            if (grunt.config(this.name).options) {
                task_options = grunt.config(this.name).options;
            }
            if (this.target) {
                target_options = grunt.config([this.name, this.target]).options;
                // merge ("extend") the two option objects
                _.assign(task_options, target_options);
            }
            this.files.forEach(function (item) {
                if (item.dest) {
                    grunt.file.write(item.dest,
                        JSON.stringify(task_options, null, 4));
                    grunt.log.writeln('File "' + item.dest + '" created.');
                }
            });
            done(true);
        }
    );

    // the default task can be run just by typing "grunt" on the command line
    grunt.registerTask('default', [
        'jsvalidate',
        JS_LINTER,
        'assemble',
        'compass',
        'prettify',
        'validation:all',
        'copy',
        'autoprefixer',
        'concat:htaccess',
        'openwebapp',
        'manifest'
    ]);

    // lint js files on all linters
    grunt.registerTask('lint', [
        'jsvalidate',
        'jshint',
        'gjslint',
        'jslint'
    ]);
};
<commit_msg>path fix for ubuntu theme css files<commit_after>// imports
var _ = require('lodash');
// gruntfile
module.exports = function (grunt) {
    'use strict';

        // development folders and files
    var SOURCE_PATH             =   'src/',
        SASS_PATH               =   SOURCE_PATH + 'styles/',
        JAVASCRIPT_PATH         =   SOURCE_PATH + 'scripts/',
        TEMPLATES_PATH          =   SOURCE_PATH + 'templates/',
        TEMPLATES_LAYOUTS_PATH  =   TEMPLATES_PATH + 'layouts/',
        TEMPLATES_PAGES_PATH    =   TEMPLATES_PATH + 'pages/',
        TEMPLATES_PARTIALS_PATH =   TEMPLATES_PATH + 'parts/',
        TEMPLATES_DATA_PATH     =   TEMPLATES_PATH + 'data/',
        HTML5_BOILERPLATE_PATH  =   'lib/html5-boilerplate/',

        SASS_FILES              =   [SASS_PATH + '**/*.scss'],
        JAVASCRIPT_SOURCES      =   ['*.js', JAVASCRIPT_PATH + '**/*.js'],
        HTACCESS_BASE_FILE      =   HTML5_BOILERPLATE_PATH + '.htaccess',
        HTACCESS_FILE           =   SOURCE_PATH + 'htaccess.conf',
        MANIFEST_WEBAPP_NAME    =   'manifest.webapp',

        // build generated output
        BUILD_PATH              =   'build/',
        HTDOCS_PATH             =   BUILD_PATH + 'www/',
        CSS_PATH                =   HTDOCS_PATH + 'css/',
        FONTS_PATH              =   HTDOCS_PATH + 'fonts/',
        JS_PATH                 =   HTDOCS_PATH + 'js/',
        CSS_FILES               =   [CSS_PATH + '**/*.css'],
        HTML_FILES              =   [HTDOCS_PATH + '**/*.html'],
        JS_FILES                =   [JS_PATH + '**/*.js'],
        FILES_TO_CACHE          =   HTML_FILES.concat(CSS_FILES, JS_FILES),

        //themes
        UBUNTU_THEME_PATH       =   'lib/ubuntu-html5-theme/0.1/ambiance/',

        // browser compatibility
        BROWSER_SUPPORT = [
            'last 2 version',
            'ff >= 18'
        ],

        // linters
        JSLINT_GLOBALS = [
            'module',
            'require',
            'console'
        ],
        JSLINT_DIRECTIVES = {
            nomen: true,
            predef: JSLINT_GLOBALS
        },
        GJSLINT_DISABLE = [
            '1',  //Extra space after "function" (Error 1)
            '6',  //Wrong indentation: expected x but got y (Error 6)
            '220' //No docs found for member (Error 220)
        ],
        GJSLINT_OPTIONS = {
            flags: [
                '--disable ' + GJSLINT_DISABLE.join(','),
                '--strict'
            ],
            reporter: {
                name: 'console'
            }
        },
        JS_LINTER = 'jslint';

// Grunt tasks
// ---------------------------------------------------------------------------
    grunt.initConfig({
        pkg: grunt.file.readJSON('package.json'),

        // Stylesheets
        compass: {
            options: {
                sassDir: SASS_PATH,
                cssDir: CSS_PATH,
                bundleExec: true,
                relativeAssets: true,
                fontsDir: FONTS_PATH
            },
            dev: {
                options: {
                    environment: 'development'
                }
            }
        },
        autoprefixer: {
            all: {
                options: {
                    browsers: BROWSER_SUPPORT
                },
                files: [{
                    expand: true,
                    cwd: CSS_PATH,
                    src: '**/*.css',
                    dest: CSS_PATH
                }]
            }
        },

        // Javascript
        jsvalidate: {
            all: JAVASCRIPT_SOURCES
        },
        jshint: {
            all: {
                src: JAVASCRIPT_SOURCES
            }
        },
        jslint: {
            all: {
                src: JAVASCRIPT_SOURCES,
                directives: JSLINT_DIRECTIVES
            }
        },

        gjslint: {
            options: GJSLINT_OPTIONS,
            all: {
                src: JAVASCRIPT_SOURCES
            }
        },

        // Apache Config
        concat: {
            htaccess: {
                files: [{
                    src: [
                        HTACCESS_BASE_FILE,
                        HTACCESS_FILE
                    ],
                    dest: HTDOCS_PATH + '.htaccess'
                }]
            }
        },

        // Templates
        assemble: {
            options: {
                flatten: true,
                layoutdir: TEMPLATES_LAYOUTS_PATH,
                partials: [TEMPLATES_PARTIALS_PATH + '**/*.hbs'],
                data: [TEMPLATES_DATA_PATH + '**/*.{json,yml}'],
                pkg: '<%= pkg %>'
            },
            pages: {
                files: [{
                    src: [
                        TEMPLATES_PAGES_PATH + '/**/*.hbs'
                    ],
                    dest: HTDOCS_PATH
                }]
            }
        },

        // Open Web App Manifest
        openwebapp: {
            options: {
                name: '<%= pkg.fullName %>',
                version: '<%= pkg.version %>',
                description: '<%= pkg.description %>',
                launch_path: '/app.html',
                developer: {
                    name: '<%= pkg.author.name %>',
                    url: '<%= pkg.author.url %>'
                },
                // icons: {
                //     "128": "/img/icons/app_128.png"
                // },
                installs_allowed_from: ['*']
            },
            all: {
                dest: HTDOCS_PATH + MANIFEST_WEBAPP_NAME
            }
        },

        //HTML indentation for generated pages
        prettify: {
            options: {
                indent: 4,
                indent_char: ' ',
                wrap_line_length: 78,
                brace_style: 'expand',
                unformatted: ['a', 'sub', 'sup', 'b', 'i', 'u']
            },
            all: {
                expand: true,
                cwd: HTDOCS_PATH,
                ext: '.html',
                src: ['**/*.html'],
                dest: HTDOCS_PATH
            }
        },

        //HTML Validation
        validation: {
            options: {
                reportpath: '.temp/validation-report.json',
                path: '.temp/validation-staus.json'
            },
            all: {
                options: {
                    reset: true
                },
                files: [{
                    src: HTML_FILES
                }]
            },
            repeat: {
                files: [{
                    src: HTML_FILES
                }]
            }
        },

        // Theme assets
        copy: {
            ubuntu_fonts: {
                files: [{
                    expand: true,
                    src: [UBUNTU_THEME_PATH + 'fonts/**/*.{ttf,woff,svg,eot}'],
                    dest: HTDOCS_PATH + 'fonts/ubuntu/',
                    flatten: true
                }]
            },
            ubuntu_css: {
                files: [{
                    expand: true,
                    src: [UBUNTU_THEME_PATH + 'css/**/*.css'],
                    dest: HTDOCS_PATH + 'css/ubuntu/',
                    flatten: true
                }]
            },
            ubuntu_img: {
                files: [{
                    expand: true,
                    src: [UBUNTU_THEME_PATH + 'img/**/*'],
                    dest: HTDOCS_PATH + 'img/ubuntu/',
                    flatten: true
                }]
            }
        },

        "regex-replace": {
            ubuntu_assets_url: {
                src: [HTDOCS_PATH + 'css/ubuntu/**/*.css'],
                actions: [
                    {
                        name: 'img_paths',
                        search: '../img/',
                        replace: '../../img/ubuntu/',
                        flags: 'g'
                    }
                ]
            }
        },

        // Appcache
        manifest: {
            generate: {
                options: {
                    network: [
                    ],
                    process: function (path) {
                        // remove documentRoot part of the cached files paths
                        return path.substring(HTDOCS_PATH.length);
                    },
                    hash: true,
                    timestamp: false,
                    basePath: ''
                },
                src: FILES_TO_CACHE,
                dest: HTDOCS_PATH + 'manifest.appcache'
            }
        },

        // Watch
        watch: {
            options: {
                spawn: false
            },
            javascript: {
                files: JAVASCRIPT_SOURCES,
                tasks: ['jsvalidate', JS_LINTER]
            },
            css: {
                files: CSS_FILES,
                tasks: ['autoprefixer'],
                options: {
                    spawn: true
                }
            },
            html: {
                files: HTML_FILES,
                tasks: ['prettify', 'validation:repeat'],
                options: {
                    spawn: true
                }
            },
            assemble: {
                files: [
                    TEMPLATES_PATH + '/**/*.hbs',
                    TEMPLATES_DATA_PATH + '**/*.{json,yml}'
                ],
                tasks: ['assemble'],
                options: {
                    spawn: true
                }
            },
            update_htaccess: {
                files: HTACCESS_FILE,
                tasks: ['concat:htaccess']
            },
            sass: {
                files: SASS_FILES,
                tasks: ['compass'],
                options: {
                    spawn: true
                }
            },
            cache: {
                files: FILES_TO_CACHE,
                tasks: ['manifest']
            }
        }
    });

    // on watch events configure certain tasks to only run on changed file
    grunt.event.on('watch', function (action, filepath) {
        if (action === 'deleted') { return; }
        grunt.config(['jsvalidate', 'all'], filepath);
        grunt.config(['jshint', 'all'], filepath);
        grunt.config(['jslint', 'all'], filepath);
        grunt.config(['gjslint', 'all'], filepath);
        grunt.config(['autoprefixer', 'all'], filepath);
    });

    //run grunt.loadNpmTasks on each grunt plugin found in your package.json
    require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
    grunt.loadNpmTasks('assemble');

    grunt.registerMultiTask(
        'openwebapp',
        'generate manifest.webapp file',
        function () {
            var task_options = {},
                target_options,
                done = this.async();
            if (grunt.config(this.name).options) {
                task_options = grunt.config(this.name).options;
            }
            if (this.target) {
                target_options = grunt.config([this.name, this.target]).options;
                // merge ("extend") the two option objects
                _.assign(task_options, target_options);
            }
            this.files.forEach(function (item) {
                if (item.dest) {
                    grunt.file.write(item.dest,
                        JSON.stringify(task_options, null, 4));
                    grunt.log.writeln('File "' + item.dest + '" created.');
                }
            });
            done(true);
        }
    );

    // the default task can be run just by typing "grunt" on the command line
    grunt.registerTask('default', [
        'jsvalidate',
        JS_LINTER,
        'assemble',
        'compass',
        'prettify',
        'validation:all',
        'copy',
        'autoprefixer',
        'regex-replace',
        'concat:htaccess',
        'openwebapp',
        'manifest'
    ]);

    // lint js files on all linters
    grunt.registerTask('lint', [
        'jsvalidate',
        'jshint',
        'gjslint',
        'jslint'
    ]);
};
<|endoftext|>


<commit_before>
/**
 * Minimal.View Limit
 * @since 0.1.9.1
 * @description Comes from Minimal View
 * @return {[type]} [description]
 */
UI.Container.implement({
	options: {
		toggleFx: {
			duration: 250,
		    transition: 'quart:out',
		    link: 'cancel'
		}
	},

	/**
	 * [_initToggle description]
	 * @return {[type]} [description]
	 */
	_initToggle: function() {
		 //_log('_initToggle', this.isOpen, this._modifier, this.element);

		var opts = this.options,
			self = this,
			modifier = 'width';

		var size = this.element[modifier];
		opts.toggleFx.property = modifier;
		this.toggleFx = this.toggleFx || new Fx.Tween(this.element, opts.toggleFx).addEvents({
			complete: function(ev) {
				//_log('toggle complete', ev);
				self.fireEvent('toggled');
			}
		});
	},

	/**
	 * Remove or Inject the Application Container in the DOM
	 * @return {[type]} [description]
	 */
	toggle: function() {
		//console.log('toggle', this.isOpen);

		var self = this,
			opts = this.options,
			modifier = 'width';

		var size = this[modifier];
		//console.log('size', size);
		_log('--- isOpen', this.isOpen);
		if (this.isOpen === true) {
			//_log('--- close', this.isOpen);
			this.toggleClose();
		} else {
			//_log('--- open', this.isOpen);
			this.toggleOpen(size);
		}

		//return this.isOpen;
	},

	/**
	 * [toggleClose description]
	 * @return {[type]} [description]
	 */
	toggleClose: function() {
		_log('toggleOpen');

		this.toggleFx.start(0);

		//console.log('close');
		this.element.removeClass('state-focus');

		/*(function() {
			self.container.setStyle('display', 'none');
		}).delay(250);*/

		minimal.settings.set('layout.' + this.main + '.hidden', true);
		minimal.settings.save();

		this.isOpen = false;

		//view.container.addClass('state-focus');
	},

	/**
	 * [toggleOpen description]
	 * @param  {[type]} size [description]
	 * @return {[type]}      [description]
	 */
	toggleOpen: function(size) {
		_log('toggleOpen', size);

		this.toggleFx.start(size);

		this.element.setStyle('display', null);
		this.element.addClass('state-focus');

		minimal.settings.set('layout.' + this.main + '.hidden', false);
		minimal.settings.save();

		//view.container.removeClass('state-focus');
		this.isOpen = true;
	}

});
<commit_msg>Fix first size for navi toggle<commit_after>
/**
 * Minimal.View Limit
 * @since 0.1.9.1
 * @description Comes from Minimal View
 * @return {[type]} [description]
 */
UI.Container.implement({
	options: {
		toggleFx: {
			duration: 300,
		    transition: 'quart:out',
		    link: 'cancel'
		}
	},

	/**
	 * [_initToggle description]
	 * @return {[type]} [description]
	 */
	_initToggle: function() {
		 _log('_initToggle', this.isOpen, this._modifier, this.element);

		var opts = this.options,
			self = this,
			modifier = 'width';

		var size = this.element[modifier];
		opts.toggleFx.property = modifier;
		this.toggleFx = this.toggleFx || new Fx.Tween(this.element, opts.toggleFx).addEvents({
			complete: function(ev) {
				//_log('toggle complete', ev);
				self.fireEvent('toggled');
			}
		});
	},

	/**
	 * Remove or Inject the Application Container in the DOM
	 * @return {[type]} [description]
	 */
	toggle: function() {
		console.log('toggle', this.isOpen);

		var self = this,
			opts = this.options,
			modifier = 'width';

		var size = this[modifier] || 160;
		console.log('size', size || 100);
		_log('--- isOpen', this.isOpen);
		if (this.isOpen === true) {
			//_log('--- close', this.isOpen);
			this.toggleClose();
		} else {
			//_log('--- open', this.isOpen);
			this.toggleOpen(size);
		}

		//return this.isOpen;
	},

	/**
	 * [toggleClose description]
	 * @return {[type]} [description]
	 */
	toggleClose: function() {
		_log('toggleOpen');

		this.toggleFx.start(0);

		//console.log('close');
		this.element.removeClass('state-focus');

		/*(function() {
			self.container.setStyle('display', 'none');
		}).delay(250);*/

		minimal.settings.set('layout.' + this.main + '.hidden', true);
		minimal.settings.save();

		this.isOpen = false;

		//view.container.addClass('state-focus');
	},

	/**
	 * [toggleOpen description]
	 * @param  {[type]} size [description]
	 * @return {[type]}      [description]
	 */
	toggleOpen: function(size) {
		_log('toggleOpen', size);

		this.toggleFx.start(size);

		this.element.setStyle('display', null);
		this.element.addClass('state-focus');

		minimal.settings.set('layout.' + this.main + '.hidden', false);
		minimal.settings.save();

		//view.container.removeClass('state-focus');
		this.isOpen = true;
	}

});
<|endoftext|>


<commit_before>var h = require('hyperscript')
var obs = require('observable')
var u = require('../util')
var pull = require('pull-stream')

exports.needs = {
  avatar_name: 'first',
  avatar_link: 'first',
  sbot_get: 'first',
  sbot_query: 'first',
  markdown: 'first',
  message_confirm: 'first',
  ting_myskills: 'first',
  ting_inquiry_adopted: 'first'
}

exports.gives = { message_content: true }

exports.create = function (api) {

  var self_id = require("../keys").id

  return {
    message_content: function (msg) {
      var c = msg.value.content
      if(c.type === 'ting-inquiry') {
        var positions_el = h('ul')

        var skillMatching = {}

        // terribly convoluted
        api.ting_myskills(self_id, function(err, mySkills) {
          if(err) { throw err; return}
          mySkills = mySkills.map(function(msg) { return msg.value.content.sk0rg })

          c.skills.forEach(function(skillID) {
            skillMatching[skillID] = obs()
            api.ting_inquiry_adopted(msg.key, skillID, function(err, grouped) {
              skillMatching[skillID](h('span',
                Object.keys(grouped).map(function(author) {
                  if (grouped[author] > 0) {
                    var el = h("span", api.avatar_link(author, api.avatar_name(author)))
                    if (author == self_id) {
                      el.appendChild( h("a",{href:"#", onclick: function(ev) {
                        ev.preventDefault()
                        api.message_confirm({
                          "type": 'ting-adopt', "adopted": false,
                          "skill": skillID, "inquiry": msg.key,
                        }, function (err, msg) {
                          if(err) {throw err; return}
                          if(!msg) return
                        })
                      }}, "(-)"))
                    }
                    return el
                  }
                })
              ))
            })

            api.sbot_get(skillID, function(err, skMsg) {
              if(err) { throw err; return}
              positions_el.appendChild(h('li',
                (mySkills.indexOf(skillID) >= 0) ?
                  h("a",{href:"#", onclick: function(ev) {
                    ev.preventDefault()
                    api.message_confirm({
                      "type": 'ting-adopt', "adopted": true,
                      "skill": skillID, "inquiry": msg.key,
                    }, function (err, msg) {
                      if(err) {throw err; return}
                      if(!msg) return
                    })
                  }}, "(+)") : null,
                skMsg.content.name+": ",
                skillMatching[skillID]
              ))
            })
          })
        });

        return h('div.inquiry',
          h('p', api.markdown(c.text)),
          h('strong','Hat: ',api.avatar_link(c.hat, api.avatar_name(c.hat))),
          h('br'),
          h('strong', 'skills needed:'),
          positions_el,
          h('strong', 'location:'),
          h("iframe", {
            "style": "width:100%",
            "frameborder":"0",
            "scrolling":"no",
            "src":"http://www.openstreetmap.org/export/embed.html?layer=mapnik&bbox="+encodeURIComponent(c.location.join(","))})
        )
      }
    }

  }
}
<commit_msg>only append location view when the data is there<commit_after>var h = require('hyperscript')
var obs = require('observable')
var u = require('../util')
var pull = require('pull-stream')

exports.needs = {
  avatar_name: 'first',
  avatar_link: 'first',
  sbot_get: 'first',
  sbot_query: 'first',
  markdown: 'first',
  message_confirm: 'first',
  ting_myskills: 'first',
  ting_inquiry_adopted: 'first'
}

exports.gives = { message_content: true }

exports.create = function (api) {

  var self_id = require("../keys").id

  return {
    message_content: function (msg) {
      var c = msg.value.content
      if(c.type === 'ting-inquiry') {
        var positions_el = h('ul')

        var skillMatching = {}

        // terribly convoluted
        api.ting_myskills(self_id, function(err, mySkills) {
          if(err) { throw err; return}
          mySkills = mySkills.map(function(msg) { return msg.value.content.sk0rg })

          c.skills.forEach(function(skillID) {
            skillMatching[skillID] = obs()
            api.ting_inquiry_adopted(msg.key, skillID, function(err, grouped) {
              skillMatching[skillID](h('span',
                Object.keys(grouped).map(function(author) {
                  if (grouped[author] > 0) {
                    var el = h("span", api.avatar_link(author, api.avatar_name(author)))
                    if (author == self_id) {
                      el.appendChild( h("a",{href:"#", onclick: function(ev) {
                        ev.preventDefault()
                        api.message_confirm({
                          "type": 'ting-adopt', "adopted": false,
                          "skill": skillID, "inquiry": msg.key,
                        }, function (err, msg) {
                          if(err) {throw err; return}
                          if(!msg) return
                        })
                      }}, "(-)"))
                    }
                    return el
                  }
                })
              ))
            })

            api.sbot_get(skillID, function(err, skMsg) {
              if(err) { throw err; return}
              positions_el.appendChild(h('li',
                (mySkills.indexOf(skillID) >= 0) ?
                  h("a",{href:"#", onclick: function(ev) {
                    ev.preventDefault()
                    api.message_confirm({
                      "type": 'ting-adopt', "adopted": true,
                      "skill": skillID, "inquiry": msg.key,
                    }, function (err, msg) {
                      if(err) {throw err; return}
                      if(!msg) return
                    })
                  }}, "(+)") : null,
                skMsg.content.name+": ",
                skillMatching[skillID]
              ))
            })
          })
        });

        var el = h('div.inquiry',
          h('p', api.markdown(c.text)),
          h('strong','Hat: ',api.avatar_link(c.hat, api.avatar_name(c.hat))),
          h('br'),
          h('strong', 'skills needed:'),
          positions_el
        )
        if (c.location && c.location.length == 4){
          el.appendChild( h('strong', 'location:'))
          el.appendChild( h("iframe", {
            "style": "width:100%",
            "frameborder":"0",
            "scrolling":"no",
            "src":"http://www.openstreetmap.org/export/embed.html?layer=mapnik&bbox="+encodeURIComponent(c.location.join(","))}))
        }

        return el
      }
    }

  }
}
<|endoftext|>


<commit_before>#!/usr/bin/env node

var fs = require('fs'),
    path = require('path'),
    wrench = require('wrench'),
    libpath = require('path'),
    program = require('commander'),
    async = require('async'),
    child = require('child_process'),
    cwd = __dirname,
    pids = [],
    pidNames = {},
    returnVal = 0;

program.command('test')
    .description('Run unit and functional tests')
    .option('-u, --unit', 'Run unit tests')
    .option('-f, --func', 'Run functional tests')
    .option('-b, --no-build', 'Don\'t build the apps')
    .option('-d, --no-deploy', 'Don\'t deploy the apps')
    .option('-s, --no-selenium', 'Don\'t run arrow_selenium')
    .option('-a, --no-arrow', 'Don\'t run arrow_server')
    .option('--logLevel <value>', 'Arrow logLevel')
    .option('--testName <value>', 'Arrow testName')
    .option('--group <value>', 'Arrow group')
    .option('--driver <value>', 'Arrow driver')
    .option('--browser <value>', 'Arrow browser')
    .option('--path <value>', 'Path to find the tests')
    .action(test);

program.command('build')
    .description('Build all HTML5 apps')
    .action(build);

program.command('deploy')
    .description('Deploy all apps')
    .action(deploy);

// report how we're called, mainly to help debug CI environments
console.log(process.argv.join(' '));
console.log();

program.parse(process.argv);

function test (cmd) {
    var series = [];
    cmd.logLevel = cmd.logLevel || 'WARN';
    // Default to all tests
    if (!cmd.unit && !cmd.func) {
        cmd.unit = true;
        cmd.func = true;
    }
    cmd.unitBrowser = cmd.unitBrowser || cmd.browser || 'firefox';
    cmd.funcBrowser = cmd.funcBrowser || cmd.browser || 'firefox';
    cmd.unitPath = path.resolve(cwd, cmd.unitPath || cmd.path || './unit');
    cmd.funcPath = path.resolve(cwd, cmd.funcPath || cmd.path || './func');
    if (cmd.arrow) {
        series.push(startArrowServer);
    }
    if (cmd.unit) {
        if ('phantomjs' !== cmd.unitBrowser) {
            if (cmd.selenium) {
                series.push(function (callback) {
                    startArrowSelenium(cmd, callback);
                });
            }
        }
        series.push(function (callback) {
            runUnitTests(cmd, callback)
        });
    }
    if (cmd.func) {
        if (cmd.build) {
            series.push(function (callback) {
                build(cmd, callback);
            });
        }
        if ('phantomjs' !== cmd.funcBrowser) {
            if (cmd.selenium) {
                series.push(function (callback) {
                    startArrowSelenium(cmd, callback);
                });
            }
        }
        if (cmd.deploy) {
            series.push(function (callback) {
                deploy(cmd, callback);
            });
        }
        series.push(function (callback) {
            runFuncTests(cmd, callback)
        });
    }
    async.series(series, finalize);
}

function startArrowServer (callback) {
    var timeout,
        listener = function (data) {
            process.stdout.write(data);
        };
    console.log("---Starting Arrow Server---");
    var p = runCommand(cwd, "node", [cwd+"/../node_modules/yahoo-arrow/arrow_server/server.js"], function () {
        // If this command returns called, then it failed to launch
        if (timeout) {
            clearTimeout(timeout);
        }
        console.log('arrow_server failed to start. If it is already running' +
            ' use \'-a\' to skip startup of arrow_server.');
        pids.pop();
        callback(1); // Trigger failure
    });
    p.stdout.on('data', listener);
    pids.push(p.pid);
    pidNames[p.pid] = 'arrow_server';
    timeout = setTimeout(function () {
        p.stdout.removeListener('data', listener); // Stop printing output from arrow_server
        callback(null);
    }, 5000);
}

function runUnitTests (cmd, callback) {
    console.log('---Running Unit Tests---');
    var arrowReportDir = cmd.unitPath + '/artifacts/arrowreport/';
    try {
        wrench.rmdirSyncRecursive(arrowReportDir);
    } catch (e) {}
    wrench.mkdirSyncRecursive(arrowReportDir);

    var commandArgs = [
        cwd + "/../node_modules/yahoo-arrow/index.js",
        cmd.unitPath + "/**/*_descriptor.json",
        "--coverage=true",
        "--report=true",
        "--reportFolder=" + arrowReportDir
    ];
    if ('phantomjs' !== cmd.unitBrowser) {
        commandArgs.push('--reuseSession');
    }
    commandArgs.push('--logLevel=' + cmd.logLevel);
    commandArgs.push('--browser=' + cmd.unitBrowser);
    cmd.driver && commandArgs.push('--driver=' + cmd.driver);
    cmd.testName && commandArgs.push('--testName=' + cmd.testName);
    cmd.group && commandArgs.push('--group=' + cmd.group);

    var p = runCommand(
        cmd.unitPath,
        "node",
        commandArgs,
        function (code) {
            callback(code);
        }
    );
    p.stdout.on('data', function (data) {
        process.stdout.write(data);
    });
}

function build (cmd, callback) {
    console.log('---Building Apps---');
    runCommand(
        cmd.funcPath + '/applications/frameworkapp/common',
        cwd + "/../bin/mojito",
        ['build', 'html5app', cmd.funcPath + '/applications/frameworkapp/flatfile'],
        callback
    );
}

function deploy (cmd, callback) {
    console.log('---Deploying Apps---');
    var appSeries = [],
        appsConfig = JSON.parse(fs.readFileSync(cmd.funcPath + '/applications/apps.json', 'utf8')),
        apps = appsConfig.applications;

    for (var i=0; i<apps.length; i++) {
        (function () {
            var app = apps[i],
                port = app.port ? parseInt(app.port) : null,
                type = app.type || 'mojito';

            if ('mojito' === type) {
                if (app.tests) {
                    var mytests = app.tests;
                    for(var j=0; j<mytests.length; j++) {
                        (function () {
                            var test = mytests[j],
                                port = test.port ? parseInt(test.port) : null;
                            appSeries.push(function (callback) {
                                runMojitoApp(cmd.funcPath + '/applications', app.path, port, test.param, callback);
                            });
                        })();
                    }
                } else if (app.enabled === "true" && app.path && port) {
                    appSeries.push(function (callback) {
                        runMojitoApp(cmd.funcPath + '/applications', app.path, port, app.param, callback);
                    });
                }
            } else if ('static' === type) {
                appSeries.push(function (callback) {
                    runStaticApp(cmd.funcPath + '/applications', app.path, port, app.param, callback);
                });
            }
        })();
    }
    async.series(appSeries, callback);
}

function startArrowSelenium (cmd, callback) {
    console.log("---Starting Arrow Selenium---");
    var commandArgs = [cwd+"/../node_modules/yahoo-arrow/arrow_selenium/selenium.js"];
    commandArgs.push("--open=" + cmd.funcBrowser);
    runCommand(cwd, "node", commandArgs, function () {
        callback(null);
    });
}

function runFuncTests (cmd, callback) {
    console.log('---Running Functional Tests---');
    var arrowReportDir = cmd.funcPath + '/artifacts/arrowreport/';
    try {
        wrench.rmdirSyncRecursive(arrowReportDir);
    } catch (e) {}
    wrench.mkdirSyncRecursive(arrowReportDir);

    var commandArgs = [
        cwd + "/../node_modules/yahoo-arrow/index.js",
        cmd.funcPath + "/**/*_descriptor.json",
        "--report=true",
        "--reportFolder=" + arrowReportDir
    ];
    if ('phantomjs' !== cmd.funcBrowser) {
        commandArgs.push('--reuseSession');
    }
    commandArgs.push('--logLevel=' + cmd.logLevel);
    commandArgs.push('--browser=' + cmd.funcBrowser);
    cmd.driver && commandArgs.push('--driver=' + cmd.driver);
    cmd.testName && commandArgs.push('--testName=' + cmd.testName);
    cmd.group && commandArgs.push('--group=' + cmd.group);

    var p = runCommand(
        cmd.funcPath,
        "node",
        commandArgs,
        function (code) {
            callback(code);
        }
    );
    p.stdout.on('data', function (data) {
        process.stdout.write(data);
    });
}

function finalize (err, results) {
    for(var i=0; i < pids.length; i++) {
        console.log('Shutting down pid ' + pids[i] + ' -- ' + pidNames[pids[i]]);
        try {
            process.kill(pids[i]);
        }
        catch(e) {
            console.log('FAILED to shut down pid ' + pids[i] + ' -- ' + pidNames[pids[i]]);
        }
    }
    if (err) {
        console.log(err);
        console.log('FAILED');
        process.exit(1);
        return;
    }
    console.log('Completed');
    process.exit(0);

}

function runCommand (path, command, argv, callback) {
    callback = callback || function () {};
    process.chdir(path);
    console.log(command + ' ' + argv.join(' '));
    var cmd = child.spawn(command, argv, {
        cwd: path,
        env: process.env
    });

    cmd.stdout.on('data', function (data) {
        // Don't care generally. But, specific commands may want a listener for this.
    });

    cmd.stderr.on('data', function (data) {
        process.stdout.write(data);
    });

    cmd.on('exit', function (code) {
        cmd.stdin.end();
        if (0 !== code) {
            callback('exit: child process exited with code ' + code);
            return;
        }
        callback(code);
    });

    cmd.on('uncaughtException', function (err) {
        process.stderr.write('uncaught exception: ' + err+'\n');
        callback(1);
    });

    return cmd;
}

function runMojitoApp (basePath, path, port, params, callback) {
    params = params || '';
    console.log('Starting ' + path + ' at port ' + port + ' with params ' + (params || 'empty'));
    var p = runCommand(basePath + '/' + path, cwd + "/../bin/mojito", ["start", port, "--context", params], function () {});
    pids.push(p.pid);
    pidNames[p.pid] = libpath.basename(path) + ':' + port + (params ? '?' + params : '');
    // Give each app a second to start
    setTimeout(function () { callback(null) }, 1000);
}

function runStaticApp (basePath, path, port, params, callback) {
    params = params || '';
    console.log('Starting static server for ' + path + ' at port ' + port);
    var p = runCommand(basePath + '/' + path, "node", [cwd + "/base/staticServer.js", port], function () {});
    pids.push(p.pid);
    pidNames[p.pid] = 'static ' + libpath.basename(path) + ':' + port;
    // Give each app a second to start
    setTimeout(function () { callback(null) }, 1000);
}
<commit_msg>--descriptor and --debugApps options, mainly useful when running by hand<commit_after>#!/usr/bin/env node

var fs = require('fs'),
    path = require('path'),
    wrench = require('wrench'),
    libpath = require('path'),
    program = require('commander'),
    async = require('async'),
    child = require('child_process'),
    cwd = __dirname,
    pids = [],
    pidNames = {},
    returnVal = 0;

program.command('test')
    .description('Run unit and functional tests')
    .option('-u, --unit', 'Run unit tests')
    .option('-f, --func', 'Run functional tests')
    .option('-b, --no-build', 'Don\'t build the apps')
    .option('-d, --no-deploy', 'Don\'t deploy the apps')
    .option('-s, --no-selenium', 'Don\'t run arrow_selenium')
    .option('-a, --no-arrow', 'Don\'t run arrow_server')
    .option('--debugApps', 'show STDOUT and STDERR from apps')
    .option('--logLevel <value>', 'Arrow logLevel')
    .option('--testName <value>', 'Arrow testName')
    .option('--descriptor <value>', 'which descriptor to run. filename (or glob) relative to --path')
    .option('--group <value>', 'Arrow group')
    .option('--driver <value>', 'Arrow driver')
    .option('--browser <value>', 'Arrow browser')
    .option('--path <value>', 'Path to find the tests. defaults to ./func or ./unit')
    .action(test);

program.command('build')
    .description('Build all HTML5 apps')
    .action(build);

program.command('deploy')
    .description('Deploy all apps')
    .action(deploy);

// report how we're called, mainly to help debug CI environments
console.log(process.argv.join(' '));
console.log();

program.parse(process.argv);

function test (cmd) {
    var series = [];
    cmd.logLevel = cmd.logLevel || 'WARN';
    // Default to all tests
    if (!cmd.unit && !cmd.func) {
        cmd.unit = true;
        cmd.func = true;
    }
    cmd.unitBrowser = cmd.unitBrowser || cmd.browser || 'firefox';
    cmd.funcBrowser = cmd.funcBrowser || cmd.browser || 'firefox';
    cmd.unitPath = path.resolve(cwd, cmd.unitPath || cmd.path || './unit');
    cmd.funcPath = path.resolve(cwd, cmd.funcPath || cmd.path || './func');
    if (cmd.arrow) {
        series.push(startArrowServer);
    }
    if (cmd.unit) {
        if ('phantomjs' !== cmd.unitBrowser) {
            if (cmd.selenium) {
                series.push(function (callback) {
                    startArrowSelenium(cmd, callback);
                });
            }
        }
        series.push(function (callback) {
            runUnitTests(cmd, callback)
        });
    }
    if (cmd.func) {
        if (cmd.build) {
            series.push(function (callback) {
                build(cmd, callback);
            });
        }
        if ('phantomjs' !== cmd.funcBrowser) {
            if (cmd.selenium) {
                series.push(function (callback) {
                    startArrowSelenium(cmd, callback);
                });
            }
        }
        if (cmd.deploy) {
            series.push(function (callback) {
                deploy(cmd, callback);
            });
        }
        series.push(function (callback) {
            runFuncTests(cmd, callback)
        });
    }
    async.series(series, finalize);
}

function startArrowServer (callback) {
    var timeout,
        listener = function (data) {
            process.stdout.write(data);
        };
    console.log("---Starting Arrow Server---");
    var p = runCommand(cwd, "node", [cwd+"/../node_modules/yahoo-arrow/arrow_server/server.js"], function () {
        // If this command returns called, then it failed to launch
        if (timeout) {
            clearTimeout(timeout);
        }
        console.log('arrow_server failed to start. If it is already running' +
            ' use \'-a\' to skip startup of arrow_server.');
        pids.pop();
        callback(1); // Trigger failure
    });
    p.stdout.on('data', listener);
    pids.push(p.pid);
    pidNames[p.pid] = 'arrow_server';
    timeout = setTimeout(function () {
        p.stdout.removeListener('data', listener); // Stop printing output from arrow_server
        callback(null);
    }, 5000);
}

function runUnitTests (cmd, callback) {
    console.log('---Running Unit Tests---');
    var arrowReportDir = cmd.unitPath + '/artifacts/arrowreport/';
    try {
        wrench.rmdirSyncRecursive(arrowReportDir);
    } catch (e) {}
    wrench.mkdirSyncRecursive(arrowReportDir);

    var commandArgs = [
        cwd + "/../node_modules/yahoo-arrow/index.js",
        cmd.unitPath + "/**/*_descriptor.json",
        "--coverage=true",
        "--report=true",
        "--reportFolder=" + arrowReportDir
    ];
    if ('phantomjs' !== cmd.unitBrowser) {
        commandArgs.push('--reuseSession');
    }
    commandArgs.push('--logLevel=' + cmd.logLevel);
    commandArgs.push('--browser=' + cmd.unitBrowser);
    cmd.driver && commandArgs.push('--driver=' + cmd.driver);
    cmd.testName && commandArgs.push('--testName=' + cmd.testName);
    cmd.group && commandArgs.push('--group=' + cmd.group);

    var p = runCommand(
        cmd.unitPath,
        "node",
        commandArgs,
        function (code) {
            callback(code);
        }
    );
    p.stdout.on('data', function (data) {
        process.stdout.write(data);
    });
}

function build (cmd, callback) {
    console.log('---Building Apps---');
    runCommand(
        cmd.funcPath + '/applications/frameworkapp/common',
        cwd + "/../bin/mojito",
        ['build', 'html5app', cmd.funcPath + '/applications/frameworkapp/flatfile'],
        callback
    );
}

function deploy (cmd, callback) {
    console.log('---Deploying Apps---');
    var appSeries = [],
        appsConfig = JSON.parse(fs.readFileSync(cmd.funcPath + '/applications/apps.json', 'utf8')),
        apps = appsConfig.applications;

    for (var i=0; i<apps.length; i++) {
        (function (app) {
            var port = app.port ? parseInt(app.port, 10) : null,
                type = app.type || 'mojito';

            if ('mojito' === type) {
                if (app.tests) {
                    var mytests = app.tests;
                    for(var j=0; j<mytests.length; j++) {
                        (function () {
                            var test = mytests[j],
                                port = test.port ? parseInt(test.port) : null;
                            appSeries.push(function (callback) {
                                runMojitoApp(cmd, cmd.funcPath + '/applications', app.path, port, test.param, callback);
                            });
                        })();
                    }
                } else if (app.enabled === "true" && app.path && port) {
                    appSeries.push(function (callback) {
                        runMojitoApp(cmd, cmd.funcPath + '/applications', app.path, port, app.param, callback);
                    });
                }
            } else if ('static' === type) {
                appSeries.push(function (callback) {
                    runStaticApp(cmd.funcPath + '/applications', app.path, port, app.param, callback);
                });
            }
        })(apps[i]);
    }
    async.series(appSeries, callback);
}

function startArrowSelenium (cmd, callback) {
    console.log("---Starting Arrow Selenium---");
    var commandArgs = [cwd+"/../node_modules/yahoo-arrow/arrow_selenium/selenium.js"];
    commandArgs.push("--open=" + cmd.funcBrowser);
    runCommand(cwd, "node", commandArgs, function () {
        callback(null);
    });
}

function runFuncTests (cmd, callback) {
    console.log('---Running Functional Tests---');
    var arrowReportDir = cmd.funcPath + '/artifacts/arrowreport/';
    try {
        wrench.rmdirSyncRecursive(arrowReportDir);
    } catch (e) {}
    wrench.mkdirSyncRecursive(arrowReportDir);

    var descriptor = cmd.descriptor || '**/*_descriptor.json';
    var commandArgs = [
        cwd + "/../node_modules/yahoo-arrow/index.js",
        cmd.funcPath + '/' + descriptor,
        "--report=true",
        "--reportFolder=" + arrowReportDir
    ];
    if ('phantomjs' !== cmd.funcBrowser) {
        commandArgs.push('--reuseSession');
    }
    commandArgs.push('--logLevel=' + cmd.logLevel);
    commandArgs.push('--browser=' + cmd.funcBrowser);
    cmd.driver && commandArgs.push('--driver=' + cmd.driver);
    cmd.testName && commandArgs.push('--testName=' + cmd.testName);
    cmd.group && commandArgs.push('--group=' + cmd.group);

    var p = runCommand(
        cmd.funcPath,
        "node",
        commandArgs,
        function (code) {
            callback(code);
        }
    );
    p.stdout.on('data', function (data) {
        process.stdout.write(data);
    });
}

function finalize (err, results) {
    for(var i=0; i < pids.length; i++) {
        console.log('Shutting down pid ' + pids[i] + ' -- ' + pidNames[pids[i]]);
        try {
            process.kill(pids[i]);
        }
        catch(e) {
            console.log('FAILED to shut down pid ' + pids[i] + ' -- ' + pidNames[pids[i]]);
        }
    }
    if (err) {
        console.log(err);
        console.log('FAILED');
        process.exit(1);
        return;
    }
    console.log('Completed');
    process.exit(0);

}

function runCommand (path, command, argv, callback) {
    callback = callback || function () {};
    process.chdir(path);
    console.log(command + ' ' + argv.join(' '));
    var cmd = child.spawn(command, argv, {
        cwd: path,
        env: process.env
    });

    cmd.stdout.on('data', function (data) {
        // Don't care generally. But, specific commands may want a listener for this.
    });

    cmd.stderr.on('data', function (data) {
        process.stdout.write(data);
    });

    cmd.on('exit', function (code) {
        cmd.stdin.end();
        if (0 !== code) {
            callback('exit: child process exited with code ' + code);
            return;
        }
        callback(code);
    });

    cmd.on('uncaughtException', function (err) {
        process.stderr.write('uncaught exception: ' + err+'\n');
        callback(1);
    });

    return cmd;
}

function runMojitoApp (cliOptions, basePath, path, port, params, callback) {
    /* useful when debugging
    var OK = {
        4081: true,
    };
    if (! OK[port]) {
        console.error('------------------------------- SKIPPING APP ON PORT ' + port);
        callback();
        return;
    }
    */

    params = params || '';
    console.log('Starting ' + path + ' at port ' + port + ' with params ' + (params || 'empty'));
    var p = runCommand(basePath + '/' + path, cwd + "/../bin/mojito", ["start", port, "--context", params], function () {});
    pids.push(p.pid);
    pidNames[p.pid] = libpath.basename(path) + ':' + port + (params ? '?' + params : '');
    // Give each app a second to start
    setTimeout(function () { callback(null) }, 1000);

    if (cliOptions.debugApps) {
        p.stdout.on('data', function(data) {
            console.error('---DEBUG ' + port + ' STDOUT--- ' + data.toString());
        });
        p.stderr.on('data', function(data) {
            console.error('---DEBUG ' + port + ' STDERR--- ' + data.toString());
        });
    }
}

function runStaticApp (basePath, path, port, params, callback) {
    params = params || '';
    console.log('Starting static server for ' + path + ' at port ' + port);
    var p = runCommand(basePath + '/' + path, "node", [cwd + "/base/staticServer.js", port], function () {});
    pids.push(p.pid);
    pidNames[p.pid] = 'static ' + libpath.basename(path) + ':' + port;
    // Give each app a second to start
    setTimeout(function () { callback(null) }, 1000);
}
<|endoftext|>