<commit_before>const colors = require('colors');
const readline = require('readline');
const OAuth2Client = require('googleapis').auth.OAuth2;

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'), { multiArgs: true });

const defaults = require('../defaults');

const GOOGLE_AUTH_SCOPE = [
  'https://www.googleapis.com/auth/drive',
  'https://www.googleapis.com/auth/drive.scripts'
];

module.exports = function auth(clientSecretPath, withWebserver) {
  return fs
    .lstatAsync(defaults.STORAGE_FILE)
    .then(() => {
      console.log(
        defaults.STORAGE_FILE +
          ' already exists. Remove it to re-authenticate node-google-apps-script'
      );
      return Promise.resolve();
    })
    .catch(err => {
      if (!clientSecretPath) {
        throw defaults.STORAGE_FILE +
          ' does not exist yet. Specify a credential file with `--auth ~/Downloads/client_secret_abcd.json`';
      }
      return performAuthenticationFlow(clientSecretPath, withWebserver);
    });
};

function performAuthenticationFlow(clientSecretPath, withWebserver) {
  return getCredentialsFromFile(clientSecretPath)
    .then(credentials => authenticateWithGoogle(credentials, withWebserver))
    .then(saveAuthenticationConfig)
    .then(cleanUp)
    .then(() => {
      console.log('Successfully Authenticated with Google Drive!'.green);
    })
    .catch(err => {
      console.log('Error running auth command'.underline.red);
      throw err;
    });
}

function getCredentialsFromFile(clientSecretPath) {
  return fs
    .readFileAsync(clientSecretPath)
    .then(JSON.parse)
    .then(credentials => {
      if (!credentials.installed) {
        throw 'Path did not include "OAuth 2.0 client ID" credentials. Please check that you downloaded the right JSON credentials.';
      }

      // Add important auth information to persist
      // return credentials
      return {
        client_id: credentials.installed.client_id,
        client_secret: credentials.installed.client_secret,
        redirect_uri: credentials.installed.redirect_uris[0],
      };
    })
    .catch(SyntaxError, err => {
      console.log('Invalid file contents'.red);
    })
    .error(err => {
      console.log('Unable to read file'.red);
      throw err;
    });
}

function authenticateWithGoogle(credentials, withWebserver) {
  return new Promise((resolve, reject) => {
    let redirect_uri = credentials.redirect_uri;
<<<<<<< HEAD
    if (withWebserver) {
=======
    if (withWebServer) {
>>>>>>> 8fa40ac... Uses single quote instead of double
      redirect_uri = 'http://localhost:' + defaults.WEBSERVER_PORT;
    }

    const oauth2Client = new OAuth2Client(
      credentials.client_id,
      credentials.client_secret,
      redirect_uri
    );
    const url = oauth2Client.generateAuthUrl({
      access_type: 'offline', // Required to receive a refresh token
      approval_prompt: 'force',
<<<<<<< HEAD
      scope: GOOGLE_AUTH_SCOPE,
=======
      scope: GOOGLE_AUTH_SCOPE
>>>>>>> 8fa40ac... Uses single quote instead of double
    });

    const getToken = code => {
      oauth2Client.getToken(code, (err, tokens) => {
        if (err) {
          reject(err);
          return;
        }
        credentials.refresh_token = tokens.refresh_token;
        resolve(credentials);
      });
    };

    console.log(
      "\nPlease visit the following url in your browser (you'll only have to do this once):"
        .cyan,
      url.green
    );

<<<<<<< HEAD
    if (withWebserver) {
=======
    if (withWebServer) {
>>>>>>> 8fa40ac... Uses single quote instead of double
      require('../miniOAuthServer')(defaults.WEBSERVER_PORT)
        .then(getToken)
        .catch(err => {
          console.log('Something went wrong with the webserver'.red);
          throw err;
        });
    } else {
      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
      });

      rl.question('Copy the provided code and paste it here: ', code => {
        rl.close();

        getToken(code);
      });
    }
  });
}

function saveAuthenticationConfig(credentials) {
  return fs.writeFileAsync(
    defaults.STORAGE_FILE,
    JSON.stringify(credentials, '', 2)
  );
}

function cleanUp() {
  process.stdin.destroy();
}
<commit_msg>Adds typedef for Credentials<commit_after>const colors = require('colors');
const readline = require('readline');
const OAuth2Client = require('googleapis').auth.OAuth2;

const Promise = require('bluebird');
const fs = Promise.promisifyAll(require('fs'), { multiArgs: true });

const defaults = require('../defaults');

const GOOGLE_AUTH_SCOPE = [
  'https://www.googleapis.com/auth/drive',
  'https://www.googleapis.com/auth/drive.scripts'
];

module.exports = function auth(clientSecretPath, withWebserver) {
  return fs
    .lstatAsync(defaults.STORAGE_FILE)
    .then(() => {
      console.log(
        defaults.STORAGE_FILE +
          ' already exists. Remove it to re-authenticate node-google-apps-script'
      );
      return Promise.resolve();
    })
    .catch(err => {
      if (!clientSecretPath) {
        throw defaults.STORAGE_FILE +
          ' does not exist yet. Specify a credential file with `--auth ~/Downloads/client_secret_abcd.json`';
      }
      return performAuthenticationFlow(clientSecretPath, withWebserver);
    });
};

function performAuthenticationFlow(clientSecretPath, withWebserver) {
  return getCredentialsFromFile(clientSecretPath)
    .then(credentials => authenticateWithGoogle(credentials, withWebserver))
    .then(saveAuthenticationConfig)
    .then(cleanUp)
    .then(() => {
      console.log('Successfully Authenticated with Google Drive!'.green);
    })
    .catch(err => {
      console.log('Error running auth command'.underline.red);
      throw err;
    });
}

/**
 * The parsed credentials
 * @typedef {Object} Credentials
 * @property {String} client_id
 * @property {String} client_secret
 * @property {String} redirect_uri
 * @property {String} [refresh_token]
 */
/**
 * Reads the content of the credentials file and returns a object
 * with the important fields
 * @param {String} clientSecretPath
 * @returns {Promise<Credentials>}
 */
function getCredentialsFromFile(clientSecretPath) {
  return fs
    .readFileAsync(clientSecretPath)
    .then(JSON.parse)
    .then(credentials => {
      if (!credentials.installed) {
        throw 'Path did not include "OAuth 2.0 client ID" credentials. Please check that you downloaded the right JSON credentials.';
      }

      // Add important auth information to persist
      // return credentials
      return {
        client_id: credentials.installed.client_id,
        client_secret: credentials.installed.client_secret,
        redirect_uri: credentials.installed.redirect_uris[0],
      };
    })
    .catch(SyntaxError, err => {
      console.log('Invalid file contents'.red);
    })
    .error(err => {
      console.log('Unable to read file'.red);
      throw err;
    });
}

/**
 * Authenticate with Google using the parsed credentials.
 * Uses the OAuth2Client to generate the AuthUrl
 * @param {Credentials} credentials
 * @param {Boolean} withWebServer
 * @returns {Promise}
 */
function authenticateWithGoogle(credentials, withWebServer) {
  return new Promise((resolve, reject) => {
    let redirect_uri = credentials.redirect_uri;
    if (withWebServer) {
      redirect_uri = 'http://localhost:' + defaults.WEBSERVER_PORT;
    }

    const oauth2Client = new OAuth2Client(
      credentials.client_id,
      credentials.client_secret,
      redirect_uri
    );
    const url = oauth2Client.generateAuthUrl({
      access_type: 'offline', // Required to receive a refresh token
      approval_prompt: 'force',
      scope: GOOGLE_AUTH_SCOPE
    });

    const getToken = code => {
      oauth2Client.getToken(code, (err, tokens) => {
        if (err) {
          reject(err);
          return;
        }
        credentials.refresh_token = tokens.refresh_token;
        resolve(credentials);
      });
    };

    console.log(
      "\nPlease visit the following url in your browser (you'll only have to do this once):"
        .cyan,
      url.green
    );

    if (withWebServer) {
      require('../miniOAuthServer')(defaults.WEBSERVER_PORT)
        .then(getToken)
        .catch(err => {
          console.log('Something went wrong with the webserver'.red);
          throw err;
        });
    } else {
      const rl = readline.createInterface({
        input: process.stdin,
        output: process.stdout,
      });

      rl.question('Copy the provided code and paste it here: ', code => {
        rl.close();

        getToken(code);
      });
    }
  });
}

/**
 * Write the contents to disk
 * @param {Credentials} credentials
 */
function saveAuthenticationConfig(credentials) {
  return fs.writeFileAsync(
    defaults.STORAGE_FILE,
    JSON.stringify(credentials, '', 2)
  );
}

function cleanUp() {
  process.stdin.destroy();
}
<|endoftext|>


<commit_before>
/*

  Views.js

*/

var Utils     = require("./utils"),
    Metadata  = require("./metadata"),
    fs        = require("fs"),
    cons      = require("consolidate"),
    _         = require("lodash/core"),
    constant  = require("lodash/constant"),
    cloneDeep = require("lodash/cloneDeep"),
    express   = require("express"),
    Promise   = require("bluebird"),
    readFile  = Promise.promisify(fs.readFile);

function addEngines(router, engines) {
  for (var ext in engines)
    router.engine(ext, makeEngine(engines[ext]));

  // Add standard html engine if not already added
  if (!router.engines["html"]) router.engine("html", makeEngine("html"));
}

function makeEngine(pipeline) {

  // Simple temlating
  if (-1 === pipeline.indexOf(">") && "html" !== pipeline) return cons[pipeline];

  return function(path, options, fn) {

    var promise = readFile(path, "utf8"),
        engines = (pipeline) ? pipeline.split(">") : [],
        engine;

    // Chain through engines
    while (engine = engines.shift())
      if ("html" !== engine) (function(eng) {
        promise = promise.then(function(str) { return cons[eng].render(str, options); })
      })(engine);

    // Return result
    promise.then(function(str) { fn(null, str ); });

  };
}

function addHtmlEngine(router) {
  if ("undefined" === typeof router.engines["html"]) {
    router.engine("html", function (path, options, fn) {
      fn(null, fs.readFileSync(path, "utf8"));
    });
  }
}

function makeDataQuery(db, queries, context) {

  if (queries) {

    var key,
        keys = _.keys(context),
        re   = new RegExp("<(" + keys.join("|") + ")>", "gi"),
        match;

    queries = cloneDeep(queries);

    queries.forEach(function(query) {
      for (key in query.filters || {}) {
        if (match = query.filters[key].match(re)) {
          match = match[0];
          query.filters[key] = query.filters[key].replace(new RegExp(match, "g"), context[match.slice(1, match.length-1)]);
        }
      }
    });

    return function() { return db.queries(queries); }
  } else
    return constant({});

}

function makeRoutes(route, file, db, query) {

  var base;

  if (!query) return Utils.makeUrls(route, file, ".html");

  // If name starts with @, treat as template for containing folder
  if ("@" === file.name[0]) {
    base = Utils.makeUrls(route, file, ".html")[0].split("/");
    base.pop();
    base = base.join("/");
  } else {
    base = Utils.makeUrls(route, file, ".html")[0];
  }

  return [base + "/:slug"];

}

module.exports = function(db, options) {

  var router = express();

  // Set defaults
  options = _.extend({ path : "./views", url : "/" }, { engines : global.engines }, options);
  addEngines(router, options.engines);

  // Walk through each file
  Utils.eachFile(options.path, { sort : true }, function(file) {

    var metadata = Metadata.extract(Utils.readFileHead(file.path, 500)),
        handler;

    handler = function(req, res) {

      // !!! Add additional context later
      var context = _.clone(req.params);
          data    = _.extend({}, global.vars, makeDataQuery(db, metadata.data, context)());

      router.render(file.path, data, function(err, html) {

        // Set cookies
        for (var key in metadata.cookies || {})
          res.cookie(key, metadata.cookies[key]);

        // Set session
        for (var key in metadata.session || {})
          req.session[key] = metadata.session[key];

        // Send response
        res.type("text/html").send(html);

      });
    }

    makeRoutes(options.url, file, db, metadata.query).forEach(function(slug) {
      router.get(slug, handler);
    });

  });

  return router;

}
<commit_msg>Views#makeDataQuery: allows for non-string types<commit_after>
/*

  Views.js

*/

var Utils     = require("./utils"),
    Metadata  = require("./metadata"),
    fs        = require("fs"),
    cons      = require("consolidate"),
    _         = require("lodash/core"),
    constant  = require("lodash/constant"),
    cloneDeep = require("lodash/cloneDeep"),
    express   = require("express"),
    Promise   = require("bluebird"),
    readFile  = Promise.promisify(fs.readFile);

function addEngines(router, engines) {
  for (var ext in engines)
    router.engine(ext, makeEngine(engines[ext]));

  // Add standard html engine if not already added
  if (!router.engines["html"]) router.engine("html", makeEngine("html"));
}

function makeEngine(pipeline) {

  // Simple temlating
  if (-1 === pipeline.indexOf(">") && "html" !== pipeline) return cons[pipeline];

  return function(path, options, fn) {

    var promise = readFile(path, "utf8"),
        engines = (pipeline) ? pipeline.split(">") : [],
        engine;

    // Chain through engines
    while (engine = engines.shift())
      if ("html" !== engine) (function(eng) {
        promise = promise.then(function(str) { return cons[eng].render(str, options); })
      })(engine);

    // Return result
    promise.then(function(str) { fn(null, str ); });

  };
}

function addHtmlEngine(router) {
  if ("undefined" === typeof router.engines["html"]) {
    router.engine("html", function (path, options, fn) {
      fn(null, fs.readFileSync(path, "utf8"));
    });
  }
}

function makeDataQuery(db, queries, context) {

  if (queries) {

    var key,
        keys = _.keys(context),
        re   = new RegExp("<(" + keys.join("|") + ")>", "gi"),
        match;

    queries = cloneDeep(queries);

    queries.forEach(function(query) {
      for (key in query.filters || {}) {
        if (_.isString(query.filters[key]) && (match = query.filters[key].match(re))) {
          match = match[0];
          query.filters[key] = query.filters[key].replace(new RegExp(match, "g"), context[match.slice(1, match.length-1)]);
        }
      }
    });

    return function() { return db.queries(queries); }
  } else
    return constant({});

}

function makeRoutes(route, file, db, query) {

  var base;

  if (!query) return Utils.makeUrls(route, file, ".html");

  // If name starts with @, treat as template for containing folder
  if ("@" === file.name[0]) {
    base = Utils.makeUrls(route, file, ".html")[0].split("/");
    base.pop();
    base = base.join("/");
  } else {
    base = Utils.makeUrls(route, file, ".html")[0];
  }

  return [base + "/:slug"];

}

module.exports = function(db, options) {

  var router = express();

  // Set defaults
  options = _.extend({ path : "./views", url : "/" }, { engines : global.engines }, options);
  addEngines(router, options.engines);

  // Walk through each file
  Utils.eachFile(options.path, { sort : true }, function(file) {

    var metadata = Metadata.extract(Utils.readFileHead(file.path, 500)),
        handler;

    handler = function(req, res) {

      // !!! Add additional context later
      var context = _.clone(req.params);
          data    = _.extend({}, global.vars, makeDataQuery(db, metadata.data, context)());

      router.render(file.path, data, function(err, html) {

        // Set cookies
        for (var key in metadata.cookies || {})
          res.cookie(key, metadata.cookies[key]);

        // Set session
        for (var key in metadata.session || {})
          req.session[key] = metadata.session[key];

        // Send response
        res.type("text/html").send(html);

      });
    }

    makeRoutes(options.url, file, db, metadata.query).forEach(function(slug) {
      router.get(slug, handler);
    });

  });

  return router;

}
<|endoftext|>


<commit_before>import React, { Component } from 'react';
import classnames from 'classnames';

// TODO:
// - remove button
// - dispatch hours override
// - total hours for selected tickets

export default class Queue extends Component {
  constructor() {
    super()
  }

  render() {
    const overBudget = ticket => ticket.actualHours > ticket.budgetHours;

    return (

Queue

{this.props.selectedTickets.length} tickets selected.

          {this.props.selectedTickets.map(ticket => (

              {ticket.id} — {ticket.company.name} — {ticket.summary} {' '}
              ({ticket.actualHours || 0} / {ticket.budgetHours || 0}) {' '}
              <button
                className="btn btn-link"
                onClick={() => this.props.onRemove(ticket.id)}
                type="button"
              >
                Remove
              </button>

          ))}

    );
  }
}

<commit_msg>Show total hours in the queue<commit_after>import React, { Component } from 'react';
import classnames from 'classnames';

// TODO:
// - dispatch hours override

export default class Queue extends Component {
  constructor() {
    super()
  }

  isOverBudget(ticket) {
    return ticket.actualHours > ticket.budgetHours;
  }

  totalBudget() {
    const { selectedTickets: tickets } = this.props;
    return tickets.map(ticket => {
      const remaining = (ticket.budgetHours || 0) - (ticket.actualHours || 0);

      if (remaining < 0) {
        return 0;
      }
      console.log({ remaining });
      return remaining;
    }).reduce((a, b) => { return a + b }, 0);
  }

  render() {
    return (

Queue ({this.totalBudget()} hours)

{this.props.selectedTickets.length} tickets selected.

          {this.props.selectedTickets.map(ticket => (

              {ticket.id} — {ticket.company.name} — {ticket.summary} {' '}
              ({ticket.actualHours || 0} / {ticket.budgetHours || 0}) {' '}
              <button
                className="btn btn-link"
                onClick={() => this.props.onRemove(ticket.id)}
                type="button"
              >
                Remove
              </button>

          ))}

    );
  }
}

<|endoftext|>


<commit_before>'use strict';

const assert = require('assert');
const calc = require('../lib/calc');

describe('calc', function () {
    it('should add numbers', function () {
        assert.equal(calc.add(3, 7), 10);
        assert.equal(calc.add(-5, 3), -2);
    });

    it('should sub numbers', function () {
        assert.equal(calc.sub(7, 4), 3);
        assert.equal(calc.sub(2, -3), 5);
    });

    it('should mul numbers', function () {
        assert.equal(calc.mul(5, 5), 25);
        assert.equal(calc.mul(-5, 5), -25);
    });

    it('should div numbers', function () {
        assert.equal(calc.div(5, 5), 1);
        assert.equal(calc.div(5, 2), 2.5);
    });
});
<commit_msg>fail test<commit_after>'use strict';

const assert = require('assert');
const calc = require('../lib/calc');

describe('calc', function () {
    it('should add numbers', function () {
        assert.equal(calc.add(3, 7), 10);
        assert.equal(calc.add(-5, 3), -1);
    });

    it('should sub numbers', function () {
        assert.equal(calc.sub(7, 4), 3);
        assert.equal(calc.sub(2, -3), 5);
    });

    it('should mul numbers', function () {
        assert.equal(calc.mul(5, 5), 25);
        assert.equal(calc.mul(-5, 5), -25);
    });

    it('should div numbers', function () {
        assert.equal(calc.div(5, 5), 1);
        assert.equal(calc.div(5, 2), 2.5);
    });
});
<|endoftext|>


<commit_before>var logger = require('npmlog');
var difftool = require('diff');

var path = require('path');

var file = require('../utils/file');

var modulesDir = path.resolve(process.cwd(), '.mauer');

function createDiff (name) {
  // TODO: validate package name
  logger.info('diff', name);
  var destDir = path.resolve(modulesDir, name);
  var source = file.read(path.join(destDir, 'source'));
  var patched = file.read(path.join(destDir, 'patched'));
  var changes = difftool.createPatch('patch', source, patched);
  process.stdout.write(changes);
}

function diff () {
  var packages = [].slice.call(arguments);
  packages.forEach(createDiff);
}

module.exports = diff;<commit_msg>more info on diff command<commit_after>var logger = require('npmlog');
var difftool = require('diff');

var path = require('path');

var file = require('../utils/file');

var modulesDir = path.resolve(process.cwd(), '.mauer');

function createDiff (name) {
  // TODO: validate package name
  logger.info('diff', name);
  var destDir = path.resolve(modulesDir, name);
  var source = file.read(path.join(destDir, 'source'));
  var patched = file.read(path.join(destDir, 'patched'));
  if (source === patched) {
    logger.warn('No changes found for the package "'+ name + '"');
  } else {
    var changes = difftool.createPatch('patch', source, patched);
    // TODO: save the patch to the patches directory
    process.stdout.write(changes);
  }
}

function diff () {
  var packages = [].slice.call(arguments);
  if (packages.length) {
    packages.forEach(createDiff);
  } else {
    logger.warn('What package do you want a diff on?');
  }
}

module.exports = diff;<|endoftext|>


<commit_before>/*exported IK*/
var IK = (function() {
  'use strict';

  function Link( length, angle ) {
    this.length = length || 0;
    this.angle = angle || 0;

    this.x = 0;
    this.y = 0;
  }

  function calculate( ik ) {
    var link;
    var next;
    var dx, dy;
    for ( var i = 0, il = ik.links.length; i < il; i++ ) {
      link = ik.links[i];
      next = ik.links[ i + 1 ];

      dx = link.x + Math.cos( link.angle ) * link.length;
      dy = link.y + Math.sin( link.angle ) * link.length;

      if ( next ) {
        next.x = dx;
        next.y = dy;
      } else {
        // Set end effector position.
        ik.xf = dx;
        ik.yf = dy;
      }
    }
  }

  function draw( ctx, ik ) {
    var x = ik.x,
        y = ik.y;

    ctx.moveTo( x, y );

    var link;
    for ( var i = 0, il = ik.links.length; i < il; i++ ) {
      link = ik.links[i];

      x += Math.cos( link.angle ) * link.length;
      y += Math.sin( link.angle ) * link.length;

      ctx.lineTo( x, y );
    }
  }

  function remove( ik, link ) {
    var index = ik.links.indexOf( link );

    if ( index !== -1 ) {
      ik.links.splice( index, 1 );
    }
  }

  /**
   * Generates an initially horizontal system from an array of link/bone
   * lengths.
   */
  function fromArray( ik, array ) {
    ik.links = array.reduce(function( links, length, index ) {
      var link = new Link( length );

      // Set initial position of link.
      var prev;
      if ( links.length > 1 ) {
        prev = links[ index - 1 ];
        link.x = prev.x + prev.length;
      }

      links.push( new Link( length ) );
      return links;
    }, [] );

    // Set end effector position.
    var last = ik.links[ ik.links.length - 1 ];
    ik.xf = last.x + last.length;
    ik.yf = 0;
  }

  return {
    Link: Link,
    calculate: calculate,
    draw: draw,
    remove: remove,
    fromArray: fromArray
  };

}) ();
<commit_msg>ik: Add length utility function.<commit_after>/*exported IK*/
var IK = (function() {
  'use strict';

  function Link( length, angle ) {
    this.length = length || 0;
    this.angle = angle || 0;

    this.x = 0;
    this.y = 0;
  }

  function length( ik ) {
    return ik.links.reduce(function( length, link ) {
      return length + link.length;
    });
  }

  function calculate( ik ) {
    var link;
    var next;
    var dx, dy;
    for ( var i = 0, il = ik.links.length; i < il; i++ ) {
      link = ik.links[i];
      next = ik.links[ i + 1 ];

      dx = link.x + Math.cos( link.angle ) * link.length;
      dy = link.y + Math.sin( link.angle ) * link.length;

      if ( next ) {
        next.x = dx;
        next.y = dy;
      } else {
        // Set end effector position.
        ik.xf = dx;
        ik.yf = dy;
      }
    }
  }

  function draw( ctx, ik ) {
    var x = ik.x,
        y = ik.y;

    ctx.moveTo( x, y );

    var link;
    for ( var i = 0, il = ik.links.length; i < il; i++ ) {
      link = ik.links[i];

      x += Math.cos( link.angle ) * link.length;
      y += Math.sin( link.angle ) * link.length;

      ctx.lineTo( x, y );
    }
  }

  function remove( ik, link ) {
    var index = ik.links.indexOf( link );

    if ( index !== -1 ) {
      ik.links.splice( index, 1 );
    }
  }

  /**
   * Generates an initially horizontal system from an array of link/bone
   * lengths.
   */
  function fromArray( ik, array ) {
    ik.links = array.reduce(function( links, length, index ) {
      var link = new Link( length );

      // Set initial position of link.
      var prev;
      if ( links.length > 1 ) {
        prev = links[ index - 1 ];
        link.x = prev.x + prev.length;
      }

      links.push( new Link( length ) );
      return links;
    }, [] );

    // Set end effector position.
    var last = ik.links[ ik.links.length - 1 ];
    ik.xf = last.x + last.length;
    ik.yf = 0;
  }

  return {
    Link: Link,
    length: length,
    calculate: calculate,
    draw: draw,
    remove: remove,
    fromArray: fromArray
  };

}) ();
<|endoftext|>


<commit_before>'use strict';

var Base = require('base-methods');
var utils = require('./utils');

/**
 * Create an instance of `Views` with the given `options`.
 *
 * ```js
 * var collection = new Views();
 * collection.addView('foo', {content: 'bar'});
 * ```
 * @param {Object} `options`
 * @api public
 */

function Views(options) {
  Base.call(this);
  options = options || {};

  // decorate the instance
  utils.renameKey(this);
  utils.option(this);

  // add constructors
  this.define('List', options.List || require('./list'));
  this.define('View', options.View || require('./view'));
  this.define('loaded', false);
  this.define('plugins', []);

  this.isCollection = true;
  this.queue = [];
  this.views = {};

  // if an instance of `List` of `Views` is passed, load it now
  if (Array.isArray(options) || options instanceof this.List) {
    this.options = options.options;
    this.addList(options.items);

  } else if (options instanceof Views) {
    this.options = options.options;
    this.addViews(options.views);

  } else {
    this.options = options;
  }
}

Base.extend(Views);

/**
 * Run a plugin on the collection instance. Plugins
 * are invoked immediately upon creating the collection
 * in the order in which they were defined.
 *
 * ```js
 * collection.use(function(views) {
 *   // `views` is the instance, as is `this`
 *
 *   // optionally return a function to be passed to
 *   // the `.use` method of each view created on the
 *   // instance
 *   return function(view) {
 *     // do stuff to each `view`
 *   };
 * });
 * ```
 *
 * @param {Function} `fn` Plugin function. If the plugin returns a function it will be passed to the `use` method of each view created on the instance.
 * @return {Object} Returns the instance for chaining.
 * @api public
 */

Views.prototype.use = function(fn) {
  var plugin = fn.call(this, this, this.options);
  if (typeof plugin === 'function') {
    this.plugins.push(plugin);
  }
  this.emit('use');
  return this;
};

/**
 * Returns a new view, using the `View` class
 * currently defined on the instance.
 *
 * ```js
 * var view = app.view('foo', {conetent: '...'});
 * // or
 * var view = app.view({path: 'foo', conetent: '...'});
 * ```
 * @name .view
 * @param {String|Object} `key` View key or object
 * @param {Object} `value` If key is a string, value is the view object.
 * @return {Object} returns the `view` object
 * @api public
 */

utils.itemFactory(Views.prototype, 'view', 'View');

/**
 * Set a view on the collection. This is identical to [addView](#addView)
 * except `setView` does not emit an event for each view.
 *
 * ```js
 * collection.setView('foo', {content: 'bar'});
 * ```
 *
 * @param {String|Object} `key` View key or object
 * @param {Object} `value` If key is a string, value is the view object.
 * @developer This method is decorated onto the collection in the constructor using the `createView` utility method.
 * @return {Object} returns the `view` instance.
 * @api public
 */

Views.prototype.setView = function(key, value) {
  var view = this.view(key, value);
  this.views[view.key] = view;
  return view;
};

/**
 * Adds event emitting and custom loading to [setView](#setView).
 *
 * @param {String} `key`
 * @param {Object} `value`
 * @api public
 */

Views.prototype.addView = function(key, value) {
  var args = [].slice.call(arguments);
  this.emit.apply(this, ['addView'].concat(args));

  var view = this.setView(key, value);
  while (this.queue.length) {
    this.setView(this.queue.shift());
  }
  return view;
};

/**
 * Load multiple views onto the collection.
 *
 * ```js
 * collection.addViews({
 *   'a.html': {content: '...'},
 *   'b.html': {content: '...'},
 *   'c.html': {content: '...'}
 * });
 * ```
 * @param {Object|Array} `views`
 * @return {Object} returns the `collection` object
 * @api public
 */

Views.prototype.addViews = function(views) {
  if (Array.isArray(views)) {
    return this.addList.apply(this, arguments);
  }
  if (arguments.length > 1 && utils.isView(arguments[1])) {
    return this.addView.apply(this, arguments);
  }

  this.emit('addViews', views);
  if (this.loaded) return this;

  this.visit('addView', views);
  return this;
};

/**
 * Load an array of views onto the collection.
 *
 * ```js
 * collection.addViews([
 *   {path: 'a.html', content: '...'},
 *   {path: 'b.html', content: '...'},
 *   {path: 'c.html', content: '...'}
 * ]);
 * ```
 * @param {Object|Array} `views`
 * @return {Object} returns the `collection` object
 * @api public
 */

Views.prototype.addList = function(list) {
  this.emit('addList', list);
  if (this.loaded) return this;

  if (!Array.isArray(list)) {
    throw new TypeError('expected list to be an array.');
  }

  var len = list.length, i = -1;
  while (++i < len) {
    var view = list[i];
    this.addView(view.path, view);
  }
  return this;
};

/**
 * Get a view from the collection.
 *
 * ```js
 * collection.getView('a.html');
 * ```
 * @param {String} `key` Key of the view to get.
 * @return {Object}
 * @api public
 */

Views.prototype.getView = function(key) {
  return this.views[key] || this.views[this.renameKey(key)];
};

/**
 * Set view types for the collection.
 *
 * @param {String} `plural` e.g. `pages`
 * @param {Object} `options`
 * @api private
 */

Views.prototype.viewType = function() {
  this.options.viewType = utils.arrayify(this.options.viewType || []);
  if (this.options.viewType.length === 0) {
    this.options.viewType.push('renderable');
  }
  return this.options.viewType;
};

/**
 * Get the number of views on the instance.
 */

utils.define(Views.prototype, 'count', {
  get: function() {
    return Object.keys(this.views).length;
  },
  set: function () {
    throw new Error('count is a read-only getter and cannot be defined.');
  }
});

/**
 * Expose `Views`
 */

module.exports = Views;
<commit_msg>remove `count` getter, corrections in code comments<commit_after>'use strict';

var Base = require('base-methods');
var utils = require('./utils');

/**
 * Create an instance of `Views` with the given `options`.
 *
 * ```js
 * var collection = new Views();
 * collection.addView('foo', {content: 'bar'});
 * ```
 * @param {Object} `options`
 * @api public
 */

function Views(options) {
  Base.call(this);
  options = options || {};

  // decorate the instance
  utils.renameKey(this);
  utils.option(this);

  // add constructors
  this.define('List', options.List || require('./list'));
  this.define('View', options.View || require('./view'));
  this.define('loaded', false);
  this.define('plugins', []);

  this.isCollection = true;
  this.queue = [];
  this.views = {};

  // if an instance of `List` of `Views` is passed, load it now
  if (Array.isArray(options) || options instanceof this.List) {
    this.options = options.options;
    this.addList(options.items);

  } else if (options instanceof Views) {
    this.options = options.options;
    this.addViews(options.views);

  } else {
    this.options = options;
  }
}

Base.extend(Views);

/**
 * Run a plugin on the collection instance. Plugins
 * are invoked immediately upon creating the collection
 * in the order in which they were defined.
 *
 * ```js
 * collection.use(function(views) {
 *   // `views` is the instance, as is `this`
 *
 *   // optionally return a function to be passed to
 *   // the `.use` method of each view created on the
 *   // instance
 *   return function(view) {
 *     // do stuff to each `view`
 *   };
 * });
 * ```
 *
 * @param {Function} `fn` Plugin function. If the plugin returns a function it will be passed to the `use` method of each view created on the instance.
 * @return {Object} Returns the instance for chaining.
 * @api public
 */

Views.prototype.use = function(fn) {
  var plugin = fn.call(this, this, this.options);
  if (typeof plugin === 'function') {
    this.plugins.push(plugin);
  }
  this.emit('use');
  return this;
};

/**
 * Returns a new view, using the `View` class
 * currently defined on the instance.
 *
 * ```js
 * var view = app.view('foo', {conetent: '...'});
 * // or
 * var view = app.view({path: 'foo', conetent: '...'});
 * ```
 * @name .view
 * @param {String|Object} `key` View key or object
 * @param {Object} `value` If key is a string, value is the view object.
 * @return {Object} returns the `view` object
 * @api public
 */

utils.itemFactory(Views.prototype, 'view', 'View');

/**
 * Set a view on the collection. This is identical to [addView](#addView)
 * except `setView` does not emit an event for each view.
 *
 * ```js
 * collection.setView('foo', {content: 'bar'});
 * ```
 *
 * @param {String|Object} `key` View key or object
 * @param {Object} `value` If key is a string, value is the view object.
 * @developer This method is decorated onto the collection in the constructor using the `createView` utility method.
 * @return {Object} returns the `view` instance.
 * @api public
 */

Views.prototype.setView = function(key, value) {
  var view = this.view(key, value);
  this.views[view.key] = view;
  return view;
};

/**
 * Adds event emitting and custom loading to [setView](#setView).
 *
 * @param {String} `key`
 * @param {Object} `value`
 * @api public
 */

Views.prototype.addView = function(key, value) {
  var args = [].slice.call(arguments);
  this.emit.apply(this, ['addView'].concat(args));

  var view = this.setView(key, value);
  while (this.queue.length) {
    this.setView(this.queue.shift());
  }
  return view;
};

/**
 * Load multiple views onto the collection.
 *
 * ```js
 * collection.addViews({
 *   'a.html': {content: '...'},
 *   'b.html': {content: '...'},
 *   'c.html': {content: '...'}
 * });
 * ```
 * @param {Object|Array} `views`
 * @return {Object} returns the `collection` object
 * @api public
 */

Views.prototype.addViews = function(views) {
  if (Array.isArray(views)) {
    return this.addList.apply(this, arguments);
  }
  if (arguments.length > 1 && utils.isView(arguments[1])) {
    return this.addView.apply(this, arguments);
  }

  this.emit('addViews', views);
  if (this.loaded) return this;

  this.visit('addView', views);
  return this;
};

/**
 * Load an array of views onto the collection.
 *
 * ```js
 * collection.addList([
 *   {path: 'a.html', content: '...'},
 *   {path: 'b.html', content: '...'},
 *   {path: 'c.html', content: '...'}
 * ]);
 * ```
 * @param {Array} `list`
 * @return {Object} returns the `views` instance
 * @api public
 */

Views.prototype.addList = function(list) {
  this.emit('addList', list);
  if (this.loaded) return this;

  if (!Array.isArray(list)) {
    throw new TypeError('expected list to be an array.');
  }

  var len = list.length, i = -1;
  while (++i < len) {
    var view = list[i];
    this.addView(view.path, view);
  }
  return this;
};

/**
 * Get a view from the collection.
 *
 * ```js
 * collection.getView('a.html');
 * ```
 * @param {String} `key` Key of the view to get.
 * @return {Object}
 * @api public
 */

Views.prototype.getView = function(key) {
  return this.views[key] || this.views[this.renameKey(key)];
};

/**
 * Set view types for the collection.
 *
 * @param {String} `plural` e.g. `pages`
 * @param {Object} `options`
 * @api private
 */

Views.prototype.viewType = function() {
  this.options.viewType = utils.arrayify(this.options.viewType || []);
  if (this.options.viewType.length === 0) {
    this.options.viewType.push('renderable');
  }
  return this.options.viewType;
};

/**
 * Expose `Views`
 */

module.exports = Views;
<|endoftext|>


<commit_before>import {
  module,
  skip,
  test
} from 'qunit';
import setupAuthentication from 'ilios/tests/helpers/setup-authentication';
import {
  click,
  find,
  findAll,
  settled,
  visit
} from '@ember/test-helpers';
import $ from 'jquery';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { getElementText, getText } from 'ilios-common';
const url = '/programs/1/programyears/1?pyObjectiveDetails=true';

module('Acceptance | Program Year - Objectives', function(hooks) {
  setupApplicationTest(hooks);
  setupMirage(hooks);
  hooks.beforeEach(async function () {
    this.school = this.server.create('school');
    this.user = await setupAuthentication({ school: this.school });
    this.server.create('program', {
      school: this.school,
    });
    this.server.create('programYear', {
      programId: 1,
    });
    this.server.create('cohort', {
      programYearId: 1
    });
    this.server.create('competency', {
      school: this.school,
    });
    this.server.create('competency', {
      parentId: 1,
      school: this.school,
      programYearIds: [1],
    });
    this.server.create('competency', {
      parentId: 1,
      school: this.school,
      programYearIds: [1],
    });
    this.server.create('competency', {
      school: this.school,
      programYearIds: [1],
    });
    this.server.create('competency', {
      school: this.school,
      programYearIds: [1],
    });
    this.server.createList('meshDescriptor', 4);

    const objective1 = this.server.create('objective', {
      programYearIds: [1],
      competencyId: 2,
      meshDescriptorIds: [1, 2]
    });
    this.server.create('objective', {
      programYearIds: [1],
      competencyId: 4
    });
    this.server.create('objective', {
      programYearIds: [1]
    });
    const course = this.server.create('course');
    this.server.create('objective', {
      courses: [course],
      parents: [objective1]
    });
  });

  test('list editable', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    await visit(url);
    assert.dom('.programyear-objective-list tbody tr').exists({ count: 3 });

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(2)'), getText('objective 0'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3)'), getText('competency 1 (competency 0)'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(4)'), getText('descriptor 0 descriptor 1'));

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(2)'), getText('objective 1'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(3)'), getText('competency 3'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(4)'), getText('Add New'));

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(2)'), getText('objective 2'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(3)'), getText('Add New'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(4)'), getText('Add New'));
  });

  test('list not editable', async function(assert) {
    await visit(url);
    assert.dom('.programyear-objective-list tbody tr').exists({ count: 3 });

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(2)'), getText('objective 0'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3)'), getText('competency 1 (competency 0)'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(4)'), getText('descriptor 0 descriptor 1'));

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(2)'), getText('objective 1'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(3)'), getText('competency 3'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(4)'), getText('None'));

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(2)'), getText('objective 2'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(3)'), getText('None'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(4)'), getText('None'));
  });

  skip('manage terms', async function() {
    //skip until we convert this test to the page object system
  });

  skip('save terms', async function() {
    //skip until we convert this test to the page object system
  });

  skip('cancel term changes', async function() {
    //skip until we convert this test to the page object system
  });

  test('manage competencies', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(14);
    await visit(url);
    let tds = findAll('.programyear-objective-list tbody tr:nth-of-type(1) td');
    assert.equal(tds.length, 4);
    await click(find('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3) .link'));
    assert.equal(await getElementText(find('.specific-title')), 'SelectParentCompetencyforObjective');
    assert.equal(await getElementText(find('.objective-manage-competency .objectivetitle')), getText('objective 0'));
    assert.equal(await getElementText(find('.objective-manage-competency .parent-picker')), getText('competency0 competency1 competency2 competency3 competency4'));
    let items = findAll('.parent-picker li.competency-title');
    assert.equal(items.length, 4);
    assert.dom('.parent-picker h5').hasClass('selected');
    assert.dom(items[0]).hasClass('selected');
    assert.ok(!find(items[1]).classList.contains('selected'));
    assert.ok(!findAll('.parent-picker h5')[1].classList.contains('selected'));
    assert.ok(!findAll('.parent-picker h5')[2].classList.contains('selected'));

    await click(findAll('.objective-manage-competency .parent-picker .clickable')[2]);
    items = findAll('.parent-picker li.competency-title');
    assert.ok(!find(items[0]).classList.contains('selected'));
    assert.ok(!find(items[1]).classList.contains('selected'));
    assert.dom(items[2]).hasClass('selected');
    assert.ok(!find(items[3]).classList.contains('selected'));
  });

  test('save competency', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(1);
    await visit(url);
    await click('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3) .link');
    let objectiveManager = find('.objective-manage-competency')[0];
    await click(findAll('.parent-picker .clickable')[1], objectiveManager);
    await click('.detail-objectives button.bigadd');
    assert.equal(await getElementText(find(findAll('.programyear-objective-list tbody tr td')[2])), getText('competency 2 (competency 0)'));
  });

  test('save no competency', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(1);
    await visit(url);
    await click('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3) .link');
    let objectiveManager = find('.objective-manage-competency')[0];
    await click(find('.parent-picker .clickable'), objectiveManager);
    await click('.detail-objectives button.bigadd');
    assert.equal(await getElementText(find(findAll('.programyear-objective-list tbody tr td')[2])), getText('Add New'));
  });

  test('cancel competency change', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(1);
    await visit(url);
    await click('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3) .link');
    let objectiveManager = find('.objective-manage-competency')[0];
    await click(findAll('.parent-picker li')[1], objectiveManager);
    await click('.detail-objectives button.bigcancel');
    assert.equal(await getElementText(find(findAll('.programyear-objective-list tbody tr td')[2])), getText('competency 1 (competency 0)'));
  });

  test('cancel remove competency change', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(1);
    await visit(url);
    await click('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3) .link');
    let objectiveManager = find('.objective-manage-competency')[0];
    await click(find('.parent-picker li'), objectiveManager);
    await click('.detail-objectives button.bigcancel');
    assert.equal(await getElementText(find(findAll('.programyear-objective-list tbody tr td')[2])), getText('competency 1 (competency 0)'));
  });

  test('add competency', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(1);
    await visit(url);
    await click('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(3) button');
    let objectiveManager = find('.objective-manage-competency')[0];
    await click(findAll('.parent-picker .clickable')[1], objectiveManager);
    await click('.detail-objectives button.bigadd');
    assert.equal(await getElementText(find(findAll('.programyear-objective-list tbody tr:nth-of-type(3) td')[2])), getText('competency 2 (competency 0)'));
  });

  test('empty objective title can not be saved', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(3);
    await visit(url);
    const container = '.programyear-objective-list';
    const title = `${container} tbody tr:nth-of-type(1) td:nth-of-type(2)`;
    const edit = `${title} .editable`;
    const editor = `${title} .fr-box`;
    const initialObjectiveTitle = 'objective 0';
    const save = `${title} .done`;
    const errorMessage = `${title} .validation-error-message`;

    assert.equal(await getElementText(title), getText(initialObjectiveTitle));
    await click(edit);

    const froala = $(editor);
    froala.froalaEditor('html.set', '

  ');
    froala.froalaEditor('undo.saveStep');
    await settled();

    assert.equal(await getElementText(errorMessage), getText('This field cannot be blank'));
    assert.dom(save).isDisabled();
  });

  test('expand objective and view links', async function(assert) {
    assert.expect(2);
    await visit(url);
    const rows = '.programyear-objective-list tbody tr';
    const objective = `${rows}:nth-of-type(1)`;
    const expand = `${objective} td:nth-of-type(1)`;
    const firstCourse = `${rows}:nth-of-type(3)`;
    const firstCourseTitle = `${firstCourse} td:nth-of-type(2)`;
    const firstCourseObjectives = `${firstCourse} td:nth-of-type(3) li`;
    const firstCourseFirstObjective = `${firstCourseObjectives}:nth-of-type(1)`;

    await click(expand);
    assert.equal(await getElementText(firstCourseTitle), getText('course 0'));
    assert.equal(await getElementText(firstCourseFirstObjective), getText('objective 3'));

  });
});
<commit_msg>Improve data fixture config<commit_after>import {
  module,
  skip,
  test
} from 'qunit';
import setupAuthentication from 'ilios/tests/helpers/setup-authentication';
import {
  click,
  find,
  findAll,
  settled,
  visit
} from '@ember/test-helpers';
import $ from 'jquery';
import { setupApplicationTest } from 'ember-qunit';
import setupMirage from 'ember-cli-mirage/test-support/setup-mirage';
import { getElementText, getText } from 'ilios-common';
const url = '/programs/1/programyears/1?pyObjectiveDetails=true';

module('Acceptance | Program Year - Objectives', function(hooks) {
  setupApplicationTest(hooks);
  setupMirage(hooks);
  hooks.beforeEach(async function () {
    this.school = this.server.create('school');
    this.user = await setupAuthentication({ school: this.school });
    const program = this.server.create('program', {
      school: this.school,
    });
    const programYear = this.server.create('programYear', {
      program,
    });
    this.server.create('cohort', {
      programYear
    });
    const parent = this.server.create('competency', {
      school: this.school,
    });
    const competency1 = this.server.create('competency', {
      parent,
      school: this.school,
      programYears: [programYear],
    });
    this.server.create('competency', {
      parent,
      school: this.school,
      programYears: [programYear],
    });
    const competency4 = this.server.create('competency', {
      school: this.school,
      programYears: [programYear],
    });
    this.server.create('competency', {
      school: this.school,
      programYears: [programYear],
    });
    const meshDescriptors = this.server.createList('meshDescriptor', 4);

    const objective1 = this.server.create('objective', {
      programYears: [programYear],
      competency: competency1,
      meshDescriptors: [meshDescriptors[0], meshDescriptors[1]]
    });
    this.server.create('objective', {
      programYears: [programYear],
      competency: competency4
    });
    this.server.create('objective', {
      programYears: [programYear]
    });
    const course = this.server.create('course');
    this.server.create('objective', {
      courses: [course],
      parents: [objective1]
    });
  });

  test('list editable', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    await visit(url);
    assert.dom('.programyear-objective-list tbody tr').exists({ count: 3 });

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(2)'), getText('objective 0'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3)'), getText('competency 1 (competency 0)'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(4)'), getText('descriptor 0 descriptor 1'));

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(2)'), getText('objective 1'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(3)'), getText('competency 3'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(4)'), getText('Add New'));

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(2)'), getText('objective 2'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(3)'), getText('Add New'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(4)'), getText('Add New'));
  });

  test('list not editable', async function(assert) {
    await visit(url);
    assert.dom('.programyear-objective-list tbody tr').exists({ count: 3 });

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(2)'), getText('objective 0'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3)'), getText('competency 1 (competency 0)'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(4)'), getText('descriptor 0 descriptor 1'));

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(2)'), getText('objective 1'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(3)'), getText('competency 3'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(2) td:nth-of-type(4)'), getText('None'));

    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(2)'), getText('objective 2'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(3)'), getText('None'));
    assert.equal(await getElementText('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(4)'), getText('None'));
  });

  skip('manage terms', async function() {
    //skip until we convert this test to the page object system
  });

  skip('save terms', async function() {
    //skip until we convert this test to the page object system
  });

  skip('cancel term changes', async function() {
    //skip until we convert this test to the page object system
  });

  test('manage competencies', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(14);
    await visit(url);
    let tds = findAll('.programyear-objective-list tbody tr:nth-of-type(1) td');
    assert.equal(tds.length, 4);
    await click(find('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3) .link'));
    assert.equal(await getElementText(find('.specific-title')), 'SelectParentCompetencyforObjective');
    assert.equal(await getElementText(find('.objective-manage-competency .objectivetitle')), getText('objective 0'));
    assert.equal(await getElementText(find('.objective-manage-competency .parent-picker')), getText('competency0 competency1 competency2 competency3 competency4'));
    let items = findAll('.parent-picker li.competency-title');
    assert.equal(items.length, 4);
    assert.dom('.parent-picker h5').hasClass('selected');
    assert.dom(items[0]).hasClass('selected');
    assert.ok(!find(items[1]).classList.contains('selected'));
    assert.ok(!findAll('.parent-picker h5')[1].classList.contains('selected'));
    assert.ok(!findAll('.parent-picker h5')[2].classList.contains('selected'));

    await click(findAll('.objective-manage-competency .parent-picker .clickable')[2]);
    items = findAll('.parent-picker li.competency-title');
    assert.ok(!find(items[0]).classList.contains('selected'));
    assert.ok(!find(items[1]).classList.contains('selected'));
    assert.dom(items[2]).hasClass('selected');
    assert.ok(!find(items[3]).classList.contains('selected'));
  });

  test('save competency', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(1);
    await visit(url);
    await click('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3) .link');
    let objectiveManager = find('.objective-manage-competency')[0];
    await click(findAll('.parent-picker .clickable')[1], objectiveManager);
    await click('.detail-objectives button.bigadd');
    assert.equal(await getElementText(find(findAll('.programyear-objective-list tbody tr td')[2])), getText('competency 2 (competency 0)'));
  });

  test('save no competency', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(1);
    await visit(url);
    await click('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3) .link');
    let objectiveManager = find('.objective-manage-competency')[0];
    await click(find('.parent-picker .clickable'), objectiveManager);
    await click('.detail-objectives button.bigadd');
    assert.equal(await getElementText(find(findAll('.programyear-objective-list tbody tr td')[2])), getText('Add New'));
  });

  test('cancel competency change', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(1);
    await visit(url);
    await click('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3) .link');
    let objectiveManager = find('.objective-manage-competency')[0];
    await click(findAll('.parent-picker li')[1], objectiveManager);
    await click('.detail-objectives button.bigcancel');
    assert.equal(await getElementText(find(findAll('.programyear-objective-list tbody tr td')[2])), getText('competency 1 (competency 0)'));
  });

  test('cancel remove competency change', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(1);
    await visit(url);
    await click('.programyear-objective-list tbody tr:nth-of-type(1) td:nth-of-type(3) .link');
    let objectiveManager = find('.objective-manage-competency')[0];
    await click(find('.parent-picker li'), objectiveManager);
    await click('.detail-objectives button.bigcancel');
    assert.equal(await getElementText(find(findAll('.programyear-objective-list tbody tr td')[2])), getText('competency 1 (competency 0)'));
  });

  test('add competency', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(1);
    await visit(url);
    await click('.programyear-objective-list tbody tr:nth-of-type(3) td:nth-of-type(3) button');
    let objectiveManager = find('.objective-manage-competency')[0];
    await click(findAll('.parent-picker .clickable')[1], objectiveManager);
    await click('.detail-objectives button.bigadd');
    assert.equal(await getElementText(find(findAll('.programyear-objective-list tbody tr:nth-of-type(3) td')[2])), getText('competency 2 (competency 0)'));
  });

  test('empty objective title can not be saved', async function(assert) {
    this.user.update({ administeredSchools: [this.school] });
    assert.expect(3);
    await visit(url);
    const container = '.programyear-objective-list';
    const title = `${container} tbody tr:nth-of-type(1) td:nth-of-type(2)`;
    const edit = `${title} .editable`;
    const editor = `${title} .fr-box`;
    const initialObjectiveTitle = 'objective 0';
    const save = `${title} .done`;
    const errorMessage = `${title} .validation-error-message`;

    assert.equal(await getElementText(title), getText(initialObjectiveTitle));
    await click(edit);

    const froala = $(editor);
    froala.froalaEditor('html.set', '

  ');
    froala.froalaEditor('undo.saveStep');
    await settled();

    assert.equal(await getElementText(errorMessage), getText('This field cannot be blank'));
    assert.dom(save).isDisabled();
  });

  test('expand objective and view links', async function(assert) {
    assert.expect(2);
    await visit(url);
    const rows = '.programyear-objective-list tbody tr';
    const objective = `${rows}:nth-of-type(1)`;
    const expand = `${objective} td:nth-of-type(1)`;
    const firstCourse = `${rows}:nth-of-type(3)`;
    const firstCourseTitle = `${firstCourse} td:nth-of-type(2)`;
    const firstCourseObjectives = `${firstCourse} td:nth-of-type(3) li`;
    const firstCourseFirstObjective = `${firstCourseObjectives}:nth-of-type(1)`;

    await click(expand);
    assert.equal(await getElementText(firstCourseTitle), getText('course 0'));
    assert.equal(await getElementText(firstCourseFirstObjective), getText('objective 3'));

  });
});
<|endoftext|>


<commit_before>/*
 * info.js: Titanium CLI info command
 *
 * Copyright (c) 2012, Appcelerator, Inc.  All Rights Reserved.
 * See the LICENSE file for more information.
 */

var appc = require('node-appc'),
	i18n = appc.i18n(__dirname),
	__ = i18n.__,
	__n = i18n.__n,
	env = appc.environ,
	mix = appc.util.mix,
	async = require('async'),
	path = require('path');

exports.desc = __('display development environment information');

exports.config = function (logger, config, cli) {
	return {
		noAuth: true,
		skipBanner: true,
		options: {
			output: {
				abbr: 'o',
				default: 'report',
				desc: __('output format'),
				values: ['report', 'json']
			}
		}
	};
};

exports.run = function (logger, config, cli) {
	// important: we must duplicate every bit of info as to not accidentally
	// break the contract of the JSON formatted output.
	async.parallel([
		function (next) {
			env.getOSInfo(function (info) {
				next(null, {
					os: {
						name: info.os,
						version: info.osver,
						architecture: info.osarch,
						numcpus: info.oscpu,
						memory: info.memory
					},
					node: {
						version: info.node
					},
					npm: {
						version: info.npm
					}
				});
			});
		},

		function (next) {
			if (process.platform === 'darwin') {
				appc.ios.detect(function (info) {
					var result = { xcode: {} };
					Object.keys(info.xcode).forEach(function (ver) {
						if (ver != '__selected__') {
							mix(result.xcode[ver] = {}, info.xcode[ver]);
						}
					});
					result.iosCerts = {
						dev: info.certs.dev,
						devNames: [].concat(info.certs.devNames),
						dist: info.certs.dist,
						distNames: [].concat(info.certs.distNames),
						wwdr: info.certs.wwdr
					};

					var pp = result.iOSProvisioningProfiles = {};
					Object.keys(info.provisioningProfiles).forEach(function (type) {
						pp[type] || (pp[type] = []);
						info.provisioningProfiles[type].forEach(function (profile) {
							pp[type].push(mix({}, profile));
						});
					});

					result.iOSKeychains = ['System Default'].concat(info.keychains);

					next(null, result);
				});
			} else {
				next(null, null);
			}
		},

		function (next) {
			appc.android.detect(function (info) {
				next(null, { android: info });
			}, config.android && config.android.sdkPath, config.android && config.android.ndkPath);
		},

		function (next) {
			var sdks = {};
			Object.keys(env.sdks).forEach(function (ver) {
				var n = env.sdks[ver];
				sdks[ver] = {
					path: n.path,
					platforms: Object.keys(n.platforms),
					githash: n.manifest ? n.manifest.githash : null,
					timestamp: n.manifest ? n.manifest.timestamp : null
				};
			});
			next(null, { titanium: sdks });
		}
	], function (err, results) {
		var data = mix.apply(null, results),
			sections,
			xcode,
			titanium,
			maxlen,
			libraries;

		if (cli.argv.output == 'json') {
			logger.log(JSON.stringify(data, null, '\t'));
		} else {
			sections = {
				'Operating System': [
					['Name', data.os.name],
					['Version', data.os.version],
					['Architecture', data.os.architecture],
					['# CPUs', data.os.numcpus],
					['Memory', (data.os.memory / 1024 / 1024 / 1024).toFixed(1) + 'GB']
				],
				'node.js': [
					['node.js Version', data.node.version],
					['npm Version', data.npm.version]
				]
			};

			if (process.platform == 'darwin') {
				xcode = sections['Xcode'] = {};
				Object.keys(data.xcode).forEach(function (ver) {
					var x = data.xcode[ver];
					xcode[x.version + ' (build ' + x.build + ')'] = [
						['Install Location', x.path],
						['Selected', x.selected],
						['iOS SDKs', x.sdks.length ? x.sdks.join(', ') : 'none'],
						['iOS Simulators', x.sims.length ? x.sims.join(', ') : 'none']
					];
				});

				sections['iOS Certificates'] = [
					['Development', data.iosCerts.devNames.length ? data.iosCerts.devNames : 'not found'],
					['Distribution', data.iosCerts.distNames.length ? data.iosCerts.distNames : 'not found'],
					['Apple WWDR', data.iosCerts.wwdr ? 'installed' : 'not found']
				];

				var pp = sections['Development iOS Provisioning Profiles'] = {};
				if (data.iOSProvisioningProfiles.development.length) {
					data.iOSProvisioningProfiles.development.forEach(function (profile) {
						pp[profile.name] = [
							['UUID', profile.uuid],
							['App Prefix', profile.appPrefix],
							['App Id', profile.appId]
						];
					});
				} else {
					pp['None'] = [];
				}

				pp = sections['Distribution iOS Provisioning Profiles'] = {};
				if (data.iOSProvisioningProfiles.distribution.length) {
					data.iOSProvisioningProfiles.distribution.forEach(function (profile) {
						pp[profile.name] = [
							['UUID', profile.uuid],
							['App Prefix', profile.appPrefix],
							['App Id', profile.appId]
						];
					});
				} else {
					pp['None'] = [];
				}

				pp = sections['Adhoc iOS Provisioning Profiles'] = {};
				if (data.iOSProvisioningProfiles.adhoc.length) {
					data.iOSProvisioningProfiles.adhoc.forEach(function (profile) {
						pp[profile.name] = [
							['UUID', profile.uuid],
							['App Prefix', profile.appPrefix],
							['App Id', profile.appId]
						];
					});
				} else {
					pp['None'] = [];
				}

				sections['iOS Keychains'] = data.iOSKeychains.map(function (keychain) {
					return [path.basename(keychain), keychain];
				});
			}

			titanium = sections['Titanium SDKs'] = {};
			Object.keys(data.titanium).forEach(function (ver) {
				var x = data.titanium[ver];
				titanium[ver] = [
					['Install Location', x.path],
					['Platforms', x.platforms.join(', ')],
					['git Hash', x.githash || 'unknown'],
					['git Timestamp', x.timestamp || 'unknown']
				];
			});

			if (data.android) {
				sections['Android'] = [
					['Android Executable', data.android.exe],
					['SDK Path', data.android.sdkPath],
					['NDK Path', data.android.ndk.path],
					['NDK Version', data.android.ndk.version],
					['JDK Version', data.android.java.version ? data.android.java.version + '_' + data.android.java.build : 'not installed']
				];

				var androidPlatforms = sections['Android Platforms'] = {},
					androidAddons = sections['Android Add-ons'] = {},
					androidAvds = sections['Android AVDs'] = {};

				(function (targets) {
					if (targets.length) {
						targets.forEach(function (targetId) {
							var target = data.android.targets[targetId];
							switch (target.type) {
								case 'Platform':
									androidPlatforms[target.id] = [
										['Name', target.name],
										['API Level', target['api-level']],
										['Revision', target.revision],
										['Skins', target.skins.join(', ')],
										['ABIs', target.abis.join(', ')],
										['Path', target.path]
									];
									break;

								case 'Add-On':
									androidAddons[target.id] = [
										['Name', target.name],
										['Vendor', target.vendor],
										['Revision', target.revision],
										['Description', target.description],
										['Skins', target.skins.join(', ')],
										['ABIs', target.abis.join(', ')],
										['Path', target.path],
										['Based On', target['based-on'] ? __('Android %s (API level %s)', target['based-on']['android-version'], target['based-on']['api-level']) : 'unknown'],
										['Libraries', target.libraries ? Object.keys(target.libraries).map(function (lib) {
											return lib + ': ' + target.libraries[lib].description + ' (' + target.libraries[lib].jar + ')';
										}) : 'none']
									];
									break;
							}
						});
					} else {
						androidPlatforms['None'] = [];
						androidAddons['None'] = [];
					}
				}(Object.keys(data.android.targets)));

				(function (avds) {
					if (avds.length) {
						avds.forEach(function (avd) {
							androidAvds[avd.name] = [
								['Path', avd.path],
								['Target', avd.target],
								['ABI', avd.abi],
								['Skin', avd.skin],
								['SD Card', avd.sdcard || 'no sd card'],
								['Based On', avd['based-on'] ? __('Android %s (API level %s)', avd['based-on']['android-version'], avd['based-on']['api-level']) : 'unknown']
							];
						});
					} else {
						androidAvds['None'] = [];
					}
				}(data.android.avds));
			}

			// find the max length of the left column
			maxlen = 0;
			Object.keys(sections).forEach(function (section) {
				var s = sections[section];
				if (Array.isArray(s)) {
					s.forEach(function (i) {
						if (i[0].length > maxlen) {
							maxlen = i[0].length;
						}
					});
				} else {
					Object.keys(s).forEach(function (i) {
						if (i.length > maxlen) {
							maxlen = i.length;
						}
						s[i].forEach(function (j) {
							if (j[0].length > maxlen) {
								maxlen = j[0].length;
							}
						});
					});
				}
			});

			logger.banner();

			// print the sections and data
			Object.keys(sections).forEach(function (section) {
				var s = sections[section];
				logger.log(section.bold);

				if (Array.isArray(s)) {
					s.forEach(function (i) {
						if (typeof i[1] === 'string') {
							logger.log('  %s = %s', appc.string.rpad(i[0], maxlen), ('' + i[1]).cyan);
						} else if (Array.isArray(i[1])) {
							logger.log('  %s = %s', appc.string.rpad(i[0], maxlen), ('' + i[1][0]).cyan);
							for (var k = 1; k < i[1].length; k++) {
								logger.log('  %s   %s', appc.string.rpad('', maxlen), ('' + i[1][k]).cyan);
							}
						}
					});
				} else {
					Object.keys(s).forEach(function (i) {
						logger.log((''+i).grey);
						s[i].forEach(function (j) {
							if (typeof j[1] === 'string') {
								logger.log('  %s = %s', appc.string.rpad(j[0], maxlen), ('' + j[1]).cyan);
							} else if (Array.isArray(j[1])) {
								logger.log('  %s = %s', appc.string.rpad(j[0], maxlen), ('' + j[1][0]).cyan);
								for (var k = 1; k < j[1].length; k++) {
									logger.log('  %s   %s', appc.string.rpad('', maxlen), ('' + j[1][k]).cyan);
								}
							}
						});
					});
				}

				logger.log();
			});
		}
	});
};
<commit_msg>[TIMOB-11436] Display unknowns in titanium info command if Android is not found.<commit_after>/*
 * info.js: Titanium CLI info command
 *
 * Copyright (c) 2012, Appcelerator, Inc.  All Rights Reserved.
 * See the LICENSE file for more information.
 */

var appc = require('node-appc'),
	i18n = appc.i18n(__dirname),
	__ = i18n.__,
	__n = i18n.__n,
	env = appc.environ,
	mix = appc.util.mix,
	async = require('async'),
	path = require('path');

exports.desc = __('display development environment information');

exports.config = function (logger, config, cli) {
	return {
		noAuth: true,
		skipBanner: true,
		options: {
			output: {
				abbr: 'o',
				default: 'report',
				desc: __('output format'),
				values: ['report', 'json']
			}
		}
	};
};

exports.run = function (logger, config, cli) {
	// important: we must duplicate every bit of info as to not accidentally
	// break the contract of the JSON formatted output.
	async.parallel([
		function (next) {
			env.getOSInfo(function (info) {
				next(null, {
					os: {
						name: info.os,
						version: info.osver,
						architecture: info.osarch,
						numcpus: info.oscpu,
						memory: info.memory
					},
					node: {
						version: info.node
					},
					npm: {
						version: info.npm
					}
				});
			});
		},

		function (next) {
			if (process.platform === 'darwin') {
				appc.ios.detect(function (info) {
					var result = { xcode: {} };
					Object.keys(info.xcode).forEach(function (ver) {
						if (ver != '__selected__') {
							mix(result.xcode[ver] = {}, info.xcode[ver]);
						}
					});
					result.iosCerts = {
						dev: info.certs.dev,
						devNames: [].concat(info.certs.devNames),
						dist: info.certs.dist,
						distNames: [].concat(info.certs.distNames),
						wwdr: info.certs.wwdr
					};

					var pp = result.iOSProvisioningProfiles = {};
					Object.keys(info.provisioningProfiles).forEach(function (type) {
						pp[type] || (pp[type] = []);
						info.provisioningProfiles[type].forEach(function (profile) {
							pp[type].push(mix({}, profile));
						});
					});

					result.iOSKeychains = ['System Default'].concat(info.keychains);

					next(null, result);
				});
			} else {
				next(null, null);
			}
		},

		function (next) {
			appc.android.detect(function (info) {
				next(null, { android: info });
			}, config.android && config.android.sdkPath, config.android && config.android.ndkPath);
		},

		function (next) {
			var sdks = {};
			Object.keys(env.sdks).forEach(function (ver) {
				var n = env.sdks[ver];
				sdks[ver] = {
					path: n.path,
					platforms: Object.keys(n.platforms),
					githash: n.manifest ? n.manifest.githash : null,
					timestamp: n.manifest ? n.manifest.timestamp : null
				};
			});
			next(null, { titanium: sdks });
		}
	], function (err, results) {
		var data = mix.apply(null, results),
			sections,
			xcode,
			titanium,
			maxlen,
			libraries;

		if (cli.argv.output == 'json') {
			logger.log(JSON.stringify(data, null, '\t'));
		} else {
			sections = {
				'Operating System': [
					['Name', data.os.name],
					['Version', data.os.version],
					['Architecture', data.os.architecture],
					['# CPUs', data.os.numcpus],
					['Memory', (data.os.memory / 1024 / 1024 / 1024).toFixed(1) + 'GB']
				],
				'node.js': [
					['node.js Version', data.node.version],
					['npm Version', data.npm.version]
				]
			};

			if (process.platform == 'darwin') {
				xcode = sections['Xcode'] = {};
				Object.keys(data.xcode).forEach(function (ver) {
					var x = data.xcode[ver];
					xcode[x.version + ' (build ' + x.build + ')'] = [
						['Install Location', x.path],
						['Selected', x.selected],
						['iOS SDKs', x.sdks.length ? x.sdks.join(', ') : 'none'],
						['iOS Simulators', x.sims.length ? x.sims.join(', ') : 'none']
					];
				});

				sections['iOS Certificates'] = [
					['Development', data.iosCerts.devNames.length ? data.iosCerts.devNames : 'not found'],
					['Distribution', data.iosCerts.distNames.length ? data.iosCerts.distNames : 'not found'],
					['Apple WWDR', data.iosCerts.wwdr ? 'installed' : 'not found']
				];

				var pp = sections['Development iOS Provisioning Profiles'] = {};
				if (data.iOSProvisioningProfiles.development.length) {
					data.iOSProvisioningProfiles.development.forEach(function (profile) {
						pp[profile.name] = [
							['UUID', profile.uuid],
							['App Prefix', profile.appPrefix],
							['App Id', profile.appId]
						];
					});
				} else {
					pp['None'] = [];
				}

				pp = sections['Distribution iOS Provisioning Profiles'] = {};
				if (data.iOSProvisioningProfiles.distribution.length) {
					data.iOSProvisioningProfiles.distribution.forEach(function (profile) {
						pp[profile.name] = [
							['UUID', profile.uuid],
							['App Prefix', profile.appPrefix],
							['App Id', profile.appId]
						];
					});
				} else {
					pp['None'] = [];
				}

				pp = sections['Adhoc iOS Provisioning Profiles'] = {};
				if (data.iOSProvisioningProfiles.adhoc.length) {
					data.iOSProvisioningProfiles.adhoc.forEach(function (profile) {
						pp[profile.name] = [
							['UUID', profile.uuid],
							['App Prefix', profile.appPrefix],
							['App Id', profile.appId]
						];
					});
				} else {
					pp['None'] = [];
				}

				sections['iOS Keychains'] = data.iOSKeychains.map(function (keychain) {
					return [path.basename(keychain), keychain];
				});
			}

			titanium = sections['Titanium SDKs'] = {};
			Object.keys(data.titanium).forEach(function (ver) {
				var x = data.titanium[ver];
				titanium[ver] = [
					['Install Location', x.path],
					['Platforms', x.platforms.join(', ')],
					['git Hash', x.githash || 'unknown'],
					['git Timestamp', x.timestamp || 'unknown']
				];
			});

			if (data.android) {
				sections['Android'] = [
					['Android Executable', data.android.exe],
					['SDK Path', data.android.sdkPath],
					['NDK Path', data.android.ndk.path],
					['NDK Version', data.android.ndk.version],
					['JDK Version', data.android.java.version ? data.android.java.version + '_' + data.android.java.build : 'not installed']
				];

				var androidPlatforms = sections['Android Platforms'] = {},
					androidAddons = sections['Android Add-ons'] = {},
					androidAvds = sections['Android AVDs'] = {};

				(function (targets) {
					if (targets.length) {
						targets.forEach(function (targetId) {
							var target = data.android.targets[targetId];
							switch (target.type) {
								case 'Platform':
									androidPlatforms[target.id] = [
										['Name', target.name],
										['API Level', target['api-level']],
										['Revision', target.revision],
										['Skins', target.skins.join(', ')],
										['ABIs', target.abis.join(', ')],
										['Path', target.path]
									];
									break;

								case 'Add-On':
									androidAddons[target.id] = [
										['Name', target.name],
										['Vendor', target.vendor],
										['Revision', target.revision],
										['Description', target.description],
										['Skins', target.skins.join(', ')],
										['ABIs', target.abis.join(', ')],
										['Path', target.path],
										['Based On', target['based-on'] ? __('Android %s (API level %s)', target['based-on']['android-version'], target['based-on']['api-level']) : 'unknown'],
										['Libraries', target.libraries ? Object.keys(target.libraries).map(function (lib) {
											return lib + ': ' + target.libraries[lib].description + ' (' + target.libraries[lib].jar + ')';
										}) : 'none']
									];
									break;
							}
						});
					} else {
						androidPlatforms['None'] = [];
						androidAddons['None'] = [];
					}
				}(Object.keys(data.android.targets)));

				(function (avds) {
					if (avds.length) {
						avds.forEach(function (avd) {
							androidAvds[avd.name] = [
								['Path', avd.path],
								['Target', avd.target],
								['ABI', avd.abi],
								['Skin', avd.skin],
								['SD Card', avd.sdcard || 'no sd card'],
								['Based On', avd['based-on'] ? __('Android %s (API level %s)', avd['based-on']['android-version'], avd['based-on']['api-level']) : 'unknown']
							];
						});
					} else {
						androidAvds['None'] = [];
					}
				}(data.android.avds));
			} else {
				sections['Android'] = [
					['Android Executable', 'not found'],
					['SDK Path', 'unknown'],
					['NDK Path', 'unknown'],
					['NDK Version', 'unknown'],
					['JDK Version', 'unknown']
				];
				sections['Android Platforms'] = { 'None': [] };
				sections['Android Add-ons'] = { 'None': [] };
				sections['Android AVDs'] = { 'None': [] };
			}

			// find the max length of the left column
			maxlen = 0;
			Object.keys(sections).forEach(function (section) {
				var s = sections[section];
				if (Array.isArray(s)) {
					s.forEach(function (i) {
						if (i[0].length > maxlen) {
							maxlen = i[0].length;
						}
					});
				} else {
					Object.keys(s).forEach(function (i) {
						if (i.length > maxlen) {
							maxlen = i.length;
						}
						s[i].forEach(function (j) {
							if (j[0].length > maxlen) {
								maxlen = j[0].length;
							}
						});
					});
				}
			});

			logger.banner();

			// print the sections and data
			Object.keys(sections).forEach(function (section) {
				var s = sections[section];
				logger.log(section.bold);

				if (Array.isArray(s)) {
					s.forEach(function (i) {
						if (typeof i[1] === 'string') {
							logger.log('  %s = %s', appc.string.rpad(i[0], maxlen), ('' + i[1]).cyan);
						} else if (Array.isArray(i[1])) {
							logger.log('  %s = %s', appc.string.rpad(i[0], maxlen), ('' + i[1][0]).cyan);
							for (var k = 1; k < i[1].length; k++) {
								logger.log('  %s   %s', appc.string.rpad('', maxlen), ('' + i[1][k]).cyan);
							}
						}
					});
				} else {
					Object.keys(s).forEach(function (i) {
						logger.log((''+i).grey);
						s[i].forEach(function (j) {
							if (typeof j[1] === 'string') {
								logger.log('  %s = %s', appc.string.rpad(j[0], maxlen), ('' + j[1]).cyan);
							} else if (Array.isArray(j[1])) {
								logger.log('  %s = %s', appc.string.rpad(j[0], maxlen), ('' + j[1][0]).cyan);
								for (var k = 1; k < j[1].length; k++) {
									logger.log('  %s   %s', appc.string.rpad('', maxlen), ('' + j[1][k]).cyan);
								}
							}
						});
					});
				}

				logger.log();
			});
		}
	});
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

var assert = require("assert");
var types = require("ast-types");
var n = types.namedTypes;
var b = types.builders;
var hoist = require("./hoist").hoist;
var Emitter = require("./emit").Emitter;

exports.transform = function(ast) {
  return types.traverse(ast, visitNode);
};

function visitNode(node) {
  if (!n.Function.check(node) || !node.generator) {
    // Note that because we are not returning false here the traversal
    // will continue into the subtree rooted at this node, as desired.
    return;
  }

  node.generator = false;

  if (node.expression) {
    // Transform expression lambdas into normal functions.
    node.expression = false;
    node.body = b.blockStatement([
      b.returnStatement(node.body)
    ]);
  }

  // TODO Ensure these identifiers are named uniquely.
  var contextId = b.identifier("$ctx");
  var argsId = b.identifier("$args");
  var wrapGeneratorId = b.identifier("wrapGenerator");
  var shouldAliasArguments = renameArguments(node, argsId);
  var vars = hoist(node);

  if (shouldAliasArguments) {
    vars = vars || b.variableDeclaration("var", []);
    vars.declarations.push(b.variableDeclarator(
      argsId, b.identifier("arguments")
    ));
  }

  var emitter = new Emitter(contextId);
  var path = new types.NodePath(node);
  emitter.explode(path.get("body"));

  var outerBody = [];

  if (vars && vars.declarations.length > 0) {
    outerBody.push(vars);
  }

  outerBody.push(b.returnStatement(
    b.callExpression(wrapGeneratorId, [
      emitter.getContextFunction(),
      b.thisExpression()
    ])
  ));

  node.body = b.blockStatement(outerBody);

  var markMethod = b.memberExpression(
    wrapGeneratorId,
    b.identifier("mark"),
    false
  );

  if (n.FunctionDeclaration.check(node)) {
    var firstStmtPath = this.parentPath.get(0);
    firstStmtPath.replace(
      b.expressionStatement(b.callExpression(markMethod, [node.id])),
      firstStmtPath.value
    );

  } else {
    n.FunctionExpression.assert(node);
    this.replace(b.callExpression(markMethod, [node]));
  }
}

function renameArguments(func, argsId) {
  var didReplaceArguments = false;
  var hasImplicitArguments = false;

  types.traverse(func, function(node) {
    if (node === func) {
      hasImplicitArguments = !this.scope.lookup("arguments");
    } else if (n.Function.check(node)) {
      return false;
    }

    if (n.Identifier.check(node) && node.name === "arguments") {
      var isMemberProperty =
        n.MemberExpression.check(this.parent.node) &&
        this.name === "property" &&
        !this.parent.node.computed;

      if (!isMemberProperty) {
        this.replace(argsId);
        didReplaceArguments = true;
        return false;
      }
    }
  });

  // If the traversal replaced any arguments identifiers, and those
  // identifiers were free variables, then we need to alias the outer
  // function's arguments object to the variable named by argsId.
  return didReplaceArguments && hasImplicitArguments;
}
<commit_msg>Prepend wrapGenerator.mark calls to closest enclosing BlockStatement.<commit_after>/**
 * Copyright (c) 2013, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 */

var assert = require("assert");
var types = require("ast-types");
var n = types.namedTypes;
var b = types.builders;
var hoist = require("./hoist").hoist;
var Emitter = require("./emit").Emitter;

exports.transform = function(ast) {
  return types.traverse(ast, visitNode);
};

function visitNode(node) {
  if (!n.Function.check(node) || !node.generator) {
    // Note that because we are not returning false here the traversal
    // will continue into the subtree rooted at this node, as desired.
    return;
  }

  node.generator = false;

  if (node.expression) {
    // Transform expression lambdas into normal functions.
    node.expression = false;
    node.body = b.blockStatement([
      b.returnStatement(node.body)
    ]);
  }

  // TODO Ensure these identifiers are named uniquely.
  var contextId = b.identifier("$ctx");
  var argsId = b.identifier("$args");
  var wrapGeneratorId = b.identifier("wrapGenerator");
  var shouldAliasArguments = renameArguments(node, argsId);
  var vars = hoist(node);

  if (shouldAliasArguments) {
    vars = vars || b.variableDeclaration("var", []);
    vars.declarations.push(b.variableDeclarator(
      argsId, b.identifier("arguments")
    ));
  }

  var emitter = new Emitter(contextId);
  var path = new types.NodePath(node);
  emitter.explode(path.get("body"));

  var outerBody = [];

  if (vars && vars.declarations.length > 0) {
    outerBody.push(vars);
  }

  outerBody.push(b.returnStatement(
    b.callExpression(wrapGeneratorId, [
      emitter.getContextFunction(),
      b.thisExpression()
    ])
  ));

  node.body = b.blockStatement(outerBody);

  var markMethod = b.memberExpression(
    wrapGeneratorId,
    b.identifier("mark"),
    false
  );

  if (n.FunctionDeclaration.check(node)) {
    var path = this.parent;

    while (path && !n.BlockStatement.check(path.value)) {
      path = path.parent;
    }

    if (path) {
      var firstStmtPath = path.get("body", 0);
      firstStmtPath.replace(
        b.expressionStatement(b.callExpression(markMethod, [node.id])),
        firstStmtPath.value
      );
    }

  } else {
    n.FunctionExpression.assert(node);
    this.replace(b.callExpression(markMethod, [node]));
  }
}

function renameArguments(func, argsId) {
  var didReplaceArguments = false;
  var hasImplicitArguments = false;

  types.traverse(func, function(node) {
    if (node === func) {
      hasImplicitArguments = !this.scope.lookup("arguments");
    } else if (n.Function.check(node)) {
      return false;
    }

    if (n.Identifier.check(node) && node.name === "arguments") {
      var isMemberProperty =
        n.MemberExpression.check(this.parent.node) &&
        this.name === "property" &&
        !this.parent.node.computed;

      if (!isMemberProperty) {
        this.replace(argsId);
        didReplaceArguments = true;
        return false;
      }
    }
  });

  // If the traversal replaced any arguments identifiers, and those
  // identifiers were free variables, then we need to alias the outer
  // function's arguments object to the variable named by argsId.
  return didReplaceArguments && hasImplicitArguments;
}
<|endoftext|>


<commit_before>var fs = require('fs');
var assert = require('assert');
var Parser = require('../lib/bang').Parser;

var ConstantClassInfo =
	Parser.start()
    .uint16be('name_index');

var ConstantFieldrefInfo =
	Parser.start()
    .uint16be('class_index')
    .uint16be('name_and_type_index');

var ConstantMethodrefInfo =
	Parser.start()
    .uint16be('class_index')
    .uint16be('name_and_type_index');

var ConstantInterfaceMethodrefInfo =
	Parser.start()
    .uint16be('class_index')
    .uint16be('name_and_type_index');

var ConstantStringInfo =
	Parser.start()
    .uint16be('string_index');

var ConstantIntegerInfo =
	Parser.start()
    .uint32be('bytes');

var ConstantFloatInfo =
	Parser.start()
    .uint32be('bytes');

var ConstantLongInfo =
	Parser.start()
    .uint32be('high_bytes')
    .uint32be('low_bytes');

var ConstantDoubleInfo =
	Parser.start()
    .uint32be('high_bytes')
    .uint32be('low_bytes');

var ConstantNameAndTypeInfo =
	Parser.start()
    .uint16be('name_index')
    .uint16be('descriptor_index');

var ConstantUtf8Info =
	Parser.start()
    .uint16be('len')
    .string('bytes', {length: 'len'});

var ConstantMethodHandleInfo =
	Parser.start()
    .uint8('reference_kind')
    .uint16be('reference_index');

var ConstantMethodTypeInfo =
	Parser.start()
    .uint16be('descriptor_index');

var ConstantInvokeDynamicInfo =
	Parser.start()
    .uint16be('bootstrap_method_attr_index')
    .uint16be('name_and_type_index');

var CpInfo =
	Parser.start()
    .uint8('tag')
    .choice('info', {
        tag: 'tag',
        choices: {
            7:  ConstantClassInfo,
            9:  ConstantFieldrefInfo,
            10: ConstantMethodrefInfo,
            11: ConstantInterfaceMethodrefInfo,
            8:  ConstantStringInfo,
            3:  ConstantIntegerInfo,
            4:  ConstantFloatInfo,
            5:  ConstantLongInfo,
            6:  ConstantDoubleInfo,
            12: ConstantNameAndTypeInfo,
            1:  ConstantUtf8Info,
            16: ConstantMethodTypeInfo,
            18: ConstantInvokeDynamicInfo
        }
    });

var ClassFile =
	Parser.start()
    .endianess('big')
    .uint32('magic')
    .uint16('minor_version')
    .uint16('major_version')
    .uint16('constant_pool_count')
    .array('cp_info', {
        type: CpInfo,
        length: function() {
            return this.constant_pool_count - 1;
        }
    });

describe('Java class file parser', function() {
    var expect = {
        magic: 0xcafebabe,
        minor_version: 0,
        major_version: 50,
        constant_pool_count: 29,
        cp_info: [
            { tag: 10, info: { class_index: 6, name_and_type_index: 15 } },
            { tag: 9, info: { class_index: 16, name_and_type_index: 17 } },
            { tag: 8, info: { string_index: 18 } },
            { tag: 10, info: { class_index: 19, name_and_type_index: 20 } },
            { tag: 7, info: { name_index: 21 } },
            { tag: 7, info: { name_index: 22 } },
            { tag: 1, info: { len: 6, bytes: '<init>' } },
            { tag: 1, info: { len: 3, bytes: '()V' } },
            { tag: 1, info: { len: 4, bytes: 'Code' } },
            { tag: 1, info: { len: 15, bytes: 'LineNumberTable' } },
            { tag: 1, info: { len: 4, bytes: 'main' } },
            { tag: 1, info: { len: 22, bytes: '([Ljava/lang/String;)V' } },
            { tag: 1, info: { len: 10, bytes: 'SourceFile' } },
            { tag: 1, info: { len: 10, bytes: 'Hello.java' } },
            { tag: 12, info: { name_index: 7, descriptor_index: 8 } },
            { tag: 7, info: { name_index: 23 } },
            { tag: 12, info: { name_index: 24, descriptor_index: 25 } },
            { tag: 1, info: { len: 13, bytes: 'Hello, world!' } },
            { tag: 7, info: { name_index: 26 } },
            { tag: 12, info: { name_index: 27, descriptor_index: 28 } },
            { tag: 1, info: { len: 5, bytes: 'Hello' } },
            { tag: 1, info: { len: 16, bytes: 'java/lang/Object' } },
            { tag: 1, info: { len: 16, bytes: 'java/lang/System' } },
            { tag: 1, info: { len: 3, bytes: 'out' } },
            { tag: 1, info: { len: 21, bytes: 'Ljava/io/PrintStream;' } },
            { tag: 1, info: { len: 19, bytes: 'java/io/PrintStream' } },
            { tag: 1, info: { len: 7, bytes: 'println' } },
            { tag: 1, info: { len: 21, bytes: '(Ljava/lang/String;)V' } }
        ]
    };

    it('should parse class file', function() {
        fs.readFile('test/Hello.class', function(err, data) {
            assert.deepEqual(ClassFile.parse(data), expect);
        });
    });
});
<commit_msg>Added assertion of class file's magic number 0xCAFEBABE<commit_after>var fs = require('fs');
var assert = require('assert');
var Parser = require('../lib/bang').Parser;

var ConstantClassInfo =
	Parser.start()
    .uint16be('name_index');

var ConstantFieldrefInfo =
	Parser.start()
    .uint16be('class_index')
    .uint16be('name_and_type_index');

var ConstantMethodrefInfo =
	Parser.start()
    .uint16be('class_index')
    .uint16be('name_and_type_index');

var ConstantInterfaceMethodrefInfo =
	Parser.start()
    .uint16be('class_index')
    .uint16be('name_and_type_index');

var ConstantStringInfo =
	Parser.start()
    .uint16be('string_index');

var ConstantIntegerInfo =
	Parser.start()
    .uint32be('bytes');

var ConstantFloatInfo =
	Parser.start()
    .uint32be('bytes');

var ConstantLongInfo =
	Parser.start()
    .uint32be('high_bytes')
    .uint32be('low_bytes');

var ConstantDoubleInfo =
	Parser.start()
    .uint32be('high_bytes')
    .uint32be('low_bytes');

var ConstantNameAndTypeInfo =
	Parser.start()
    .uint16be('name_index')
    .uint16be('descriptor_index');

var ConstantUtf8Info =
	Parser.start()
    .uint16be('len')
    .string('bytes', {length: 'len'});

var ConstantMethodHandleInfo =
	Parser.start()
    .uint8('reference_kind')
    .uint16be('reference_index');

var ConstantMethodTypeInfo =
	Parser.start()
    .uint16be('descriptor_index');

var ConstantInvokeDynamicInfo =
	Parser.start()
    .uint16be('bootstrap_method_attr_index')
    .uint16be('name_and_type_index');

var CpInfo =
	Parser.start()
    .uint8('tag')
    .choice('info', {
        tag: 'tag',
        choices: {
            7:  ConstantClassInfo,
            9:  ConstantFieldrefInfo,
            10: ConstantMethodrefInfo,
            11: ConstantInterfaceMethodrefInfo,
            8:  ConstantStringInfo,
            3:  ConstantIntegerInfo,
            4:  ConstantFloatInfo,
            5:  ConstantLongInfo,
            6:  ConstantDoubleInfo,
            12: ConstantNameAndTypeInfo,
            1:  ConstantUtf8Info,
            16: ConstantMethodTypeInfo,
            18: ConstantInvokeDynamicInfo
        }
    });

var ClassFile =
	Parser.start()
    .endianess('big')
    .uint32('magic', {assert: function(x) {return x === 0xcafebabe; }})
    .uint16('minor_version')
    .uint16('major_version')
    .uint16('constant_pool_count')
    .array('cp_info', {
        type: CpInfo,
        length: function() {
            return this.constant_pool_count - 1;
        }
    });

describe('Java class file parser', function() {
    var expect = {
        magic: 0xcafebabe,
        minor_version: 0,
        major_version: 50,
        constant_pool_count: 29,
        cp_info: [
            { tag: 10, info: { class_index: 6, name_and_type_index: 15 } },
            { tag: 9, info: { class_index: 16, name_and_type_index: 17 } },
            { tag: 8, info: { string_index: 18 } },
            { tag: 10, info: { class_index: 19, name_and_type_index: 20 } },
            { tag: 7, info: { name_index: 21 } },
            { tag: 7, info: { name_index: 22 } },
            { tag: 1, info: { len: 6, bytes: '<init>' } },
            { tag: 1, info: { len: 3, bytes: '()V' } },
            { tag: 1, info: { len: 4, bytes: 'Code' } },
            { tag: 1, info: { len: 15, bytes: 'LineNumberTable' } },
            { tag: 1, info: { len: 4, bytes: 'main' } },
            { tag: 1, info: { len: 22, bytes: '([Ljava/lang/String;)V' } },
            { tag: 1, info: { len: 10, bytes: 'SourceFile' } },
            { tag: 1, info: { len: 10, bytes: 'Hello.java' } },
            { tag: 12, info: { name_index: 7, descriptor_index: 8 } },
            { tag: 7, info: { name_index: 23 } },
            { tag: 12, info: { name_index: 24, descriptor_index: 25 } },
            { tag: 1, info: { len: 13, bytes: 'Hello, world!' } },
            { tag: 7, info: { name_index: 26 } },
            { tag: 12, info: { name_index: 27, descriptor_index: 28 } },
            { tag: 1, info: { len: 5, bytes: 'Hello' } },
            { tag: 1, info: { len: 16, bytes: 'java/lang/Object' } },
            { tag: 1, info: { len: 16, bytes: 'java/lang/System' } },
            { tag: 1, info: { len: 3, bytes: 'out' } },
            { tag: 1, info: { len: 21, bytes: 'Ljava/io/PrintStream;' } },
            { tag: 1, info: { len: 19, bytes: 'java/io/PrintStream' } },
            { tag: 1, info: { len: 7, bytes: 'println' } },
            { tag: 1, info: { len: 21, bytes: '(Ljava/lang/String;)V' } }
        ]
    };

    it('should parse class file', function() {
        fs.readFile('test/Hello.class', function(err, data) {
            assert.deepEqual(ClassFile.parse(data), expect);
        });
    });
});
<|endoftext|>


<commit_before>var chai = require('chai');
var expect = chai.expect;

var Client = require('../index.js');
var client = new Client();
var api_key = 'demo';
client.useKey(api_key);

describe('Companies', function () {

	describe('Get Recently Created', function(){
		it('Should return recently created companies', function (done) {
			client.companies.getRecentlyCreated(function(err, data, res) {
			  if (err) { throw err; }
				expect(res.statusCode).to.equal(200);
				expect(data).to.be.defined;
				expect(data.results).to.be.defined;
				done();
			})
		});
	})

});
<commit_msg>Refined companies test to have type based checking to data.<commit_after>var chai = require('chai');
var expect = chai.expect;

var Client = require('../index.js');
var client = new Client();
var api_key = 'demo';
client.useKey(api_key);

describe('Companies', function () {

	describe('Get Recently Created', function(){
		it('Should return recently created companies', function (done) {
			client.companies.getRecentlyCreated(function(err, data, res) {
			  if (err) { throw err; }
				expect(res.statusCode).to.equal(200);
				expect(data).to.be.a('object');
				expect(data.results).to.be.a('array');
        done();
			})
		});
	})

});
<|endoftext|>