import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import MarkupFrame from '../src';

describe( '<MarkupFrame />', () => {
	it( 'renders an iframe', () => {
		const wrapper = mount( <MarkupFrame markup="" /> );
		expect( wrapper.some( 'iframe' ) ).to.equal( true );
	} );

	it( 'injects props.markup into the iframe' );

	it( 'calls props.onLoad with the iframe document' );
} );

import React from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import MarkupFrame from '../src';

describe( '<MarkupFrame />', function() {
	it( 'renders an iframe', function() {
		const wrapper = mount( <MarkupFrame markup="" /> );
		expect( wrapper.find( 'iframe' ) ).to.have.length( 1 );
	} );

	it( 'injects props.markup into the iframe', function() {
		const wrapper = mount( <MarkupFrame markup="
Hello World
" /> );
		expect( wrapper.find( 'iframe' ).get( 0 ).innerHTML ).to.eql( '
Hello World
' )
	} );

	it( 'calls props.onLoad with the iframe document', function() {
		const onLoad = doc => doc.querySelector( 'h1' ).innerHTML = 'greetings';
		const wrapper = mount( <MarkupFrame markup="
Hello World
" onLoad={ onLoad } /> );
		expect( wrapper.find( 'iframe' ).get( 0 ).innerHTML ).to.eql( '
greetings
' )
	} );
} );

Update tests to cover most common cases


console.log('Tokimeki Memorial');

var host = window.document.location.host.replace(/:.*/, '');
var ws = new WebSocket('ws://' + host + ':8000');

ws.onmessage = function (event) {
    //console.log(event);
    //console.log(JSON.parse(event.data));
};

Make WS connection in index page for testing purposes.


import "mojave/polyfill/Promise";
import "unfetch/polyfill";

export default window.fetch;

import "mojave/polyfill/promise";
import "unfetch/polyfill";

export default window.fetch;

Fix case of promise polyfill


import React, { Component, PropTypes } from 'react';

class MembersDetail extends Component {

  render() {
    const {displayName} = this.props;
    return (

{displayName}

    );
  }
}

MembersDetail.propTypes = {
  displayName: PropTypes.string.isRequired,
};

export default MembersDetail;

import React, { Component, PropTypes } from 'react';

class MembersDetail extends Component {

  render() {
    const {displayName, image} = this.props;
    return (

{displayName}

    );
  }
}

MembersDetail.propTypes = {
  displayName: PropTypes.string.isRequired,
  image: PropTypes.shape({
    uri: PropTypes.string.isRequired,
    height: PropTypes.number,
    width: PropTypes.number,
  }),
  intro: PropTypes.string,
  slug: PropTypes.string.isRequired,
  usState: PropTypes.string,
};

export default MembersDetail;

Add image and proptypes for member detail page


export default {
  state: {
    isVisible: false,
    options: {
      text: '',
      autoDismiss: true,
    },
  },
  getters: {
    snackbarIsVisible(state) {
      return state.isVisible;
    },
    snackbarOptions(state) {
      return state.options;
    },
  },
  mutations: {
    CORE_CREATE_SNACKBAR(state, snackbarOptions = {}) {
      // reset
      state.isVisible = false;
      state.options = {};
      // set new options
      state.isVisible = true;
      state.options = snackbarOptions;
    },
    CORE_CLEAR_SNACKBAR(state) {
      state.isVisible = false;
      state.options = {};
    },
    CORE_SET_SNACKBAR_TEXT(state, text) {
      state.options.text = text;
    },
  },
};

export default {
  state: {
    isVisible: false,
    options: {
      text: '',
      autoDismiss: true,
    },
  },
  getters: {
    snackbarIsVisible(state) {
      return state.isVisible;
    },
    snackbarOptions(state) {
      return state.options;
    },
  },
  mutations: {
    CORE_CREATE_SNACKBAR(state, snackbarOptions = {}) {
      // reset
      state.isVisible = false;
      state.options = {};
      // set new options
      state.isVisible = true;
      // options include text, autoDismiss, duration, actionText, actionCallback,
      // hideCallback
      state.options = snackbarOptions;
    },
    CORE_CLEAR_SNACKBAR(state) {
      state.isVisible = false;
      state.options = {};
    },
    CORE_SET_SNACKBAR_TEXT(state, text) {
      state.options.text = text;
    },
  },
};

Add comment about autodismiss options


import React from 'react';
import { connect } from 'react-redux';

const DocumentListItem = ({ title, collection, schema }) => (
  <tr className={`result result--${schema}`}>
    { title }
    { collection && collection.label }

);

const PersonListItem = ({ name, collection, schema }) => (
  <tr className={`result result--${schema}`}>
    { name }
    { collection && collection.label }

);

const ListItems = {
  'Document': DocumentListItem,
  'Person': PersonListItem,
  'LegalEntity': PersonListItem,
  'Company': PersonListItem
};

const SearchResultListItem = ({ result, collection }) => {
  const ListItem = ListItems[result.schema];
  return <ListItem collection={collection} {...result} />;
};

const mapStateToProps = ({ collections }, { result }) => ({
  collection: collections[result.collection_id]
});

export default connect(mapStateToProps)(SearchResultListItem);

import React from 'react';
import { connect } from 'react-redux';

const DocumentListItem = ({ title, collection, schema }) => (
  <tr className={`result result--${schema}`}>
    { title }
    { collection && collection.label }

);

const PersonListItem = ({ name, collection, schema }) => (
  <tr className={`result result--${schema}`}>
    { name }

        { collection ?
            collection.label :
            Loading collection
        }

);

const ListItems = {
  'Document': DocumentListItem,
  'Person': PersonListItem,
  'LegalEntity': PersonListItem,
  'Company': PersonListItem
};

const SearchResultListItem = ({ result, collection }) => {
  const ListItem = ListItems[result.schema];
  return <ListItem collection={collection} {...result} />;
};

const mapStateToProps = ({ collections }, { result }) => ({
  collection: collections[result.collection_id]
});

export default connect(mapStateToProps)(SearchResultListItem);

Add placeholder for loading collection names


$(document).ready(function() {
    $('#global-modal-container').on('show.bs.modal', function (event) {
        sourceUrl = $(event.relatedTarget).attr('href');
        getGlobalModalHtml(sourceUrl);
    });
});

function getGlobalModalHtml(url) {
    $.ajax({
        url: url,
        type: "post",

        success: function(returnData) {

            $('#global-modal-container .modal-body').html(returnData);
            headerHtml = $('#global-modal-container .modal-body .apc-modal-header')[0].outerHTML;
            $('#global-modal-container .modal-body .apc-modal-header').remove();
            $('#global-modal-container .modal-header .apc-modal-header').remove();
            $('#global-modal-container .modal-header').prepend(headerHtml);

            //alert('success');
        },

        error: function(jqXHR, textStatus, errorThrown){
            alert('Error loading Global Modal Container: ' + textStatus + ':' + errorThrown);
        }

    });
}

$(document).ready(function() {
    $('#global-modal-container').on('show.bs.modal', function (event) {
        sourceUrl = $(event.relatedTarget).attr('href');
        getGlobalModalHtml(sourceUrl);
        $('.tooltip').hide();
    });
});

function getGlobalModalHtml(url) {
    $.ajax({
        url: url,
        type: "post",

        success: function(returnData) {

            $('#global-modal-container .modal-body').html(returnData);
            headerHtml = $('#global-modal-container .modal-body .apc-modal-header')[0].outerHTML;
            $('#global-modal-container .modal-body .apc-modal-header').remove();
            $('#global-modal-container .modal-header .apc-modal-header').remove();
            $('#global-modal-container .modal-header').prepend(headerHtml);

            //alert('success');
        },

        error: function(jqXHR, textStatus, errorThrown){
            alert('Error loading Global Modal Container: ' + textStatus + ':' + errorThrown);
        }

    });
}

Hide all tooltips when showing a modal window


const sass = require('node-sass');
const path = require('path');
const config = require('../config');
const fs = require('fs');
const imdino = require('imdi-no');

const development = config.env === 'development';

module.exports = {
  "/stylesheets/main.css": function (callback) {

    const opts = {
      file:               path.join(__dirname, "/../stylesheets/main.scss"),
      outFile:            '/stylesheets/main.css',
      includePaths:       imdino.includePaths,

      sourceMap:          development,
      sourceMapEmbed:     development,
      sourceMapContents:  development,
      sourceComments:     development,
      outputStyle:        development ? 'nested' : 'compressed'
    };

    sass.render(opts, (err, result)=> {
      if (err) {
        return callback(err)
      }
      callback(null, result.css);
    });
  }
};

fs.readdirSync(imdino.paths.gfx).forEach(file => {

  const fullPath = path.join(imdino.paths.gfx, file);
  const httpPaths = [
    path.join('/imdi-no/_themes/blank/gfx', file),
    path.join('/gfx', file)
  ];

  httpPaths.forEach(httpPath => {
    module.exports[httpPath] = function() {
      return fs.createReadStream(fullPath);
    }
  });
});

const sass = require('node-sass');
const path = require('path');
const config = require('../config');
const fs = require('fs');
const imdino = require('imdi-no');

const development = config.env === 'development';

module.exports = {
  "/stylesheets/main.css": function (callback) {

    const opts = {
      file:               require.resolve("../stylesheets/main.scss"),
      outFile:            '/stylesheets/main.css',
      includePaths:       imdino.includePaths,

      sourceMap:          development,
      sourceMapEmbed:     development,
      sourceMapContents:  development,
      sourceComments:     development,
      outputStyle:        development ? 'nested' : 'compressed'
    };

    sass.render(opts, (err, result)=> {
      if (err) {
        return callback(err)
      }
      callback(null, result.css);
    });
  }
};

fs.readdirSync(imdino.paths.gfx).forEach(file => {

  const fullPath = path.join(imdino.paths.gfx, file);
  const httpPaths = [
    path.join('/imdi-no/_themes/blank/gfx', file),
    path.join('/gfx', file)
  ];

  httpPaths.forEach(httpPath => {
    module.exports[httpPath] = function() {
      return fs.createReadStream(fullPath);
    }
  });
});

Use require.resolve to locate main.scss


module.exports = [
  {
    "_key": "default:token-123",
    "token": "xyz",
    "scope": ["oada-rocks:all"],
    "createTime": 1413831649937,
    "expiresIn": 60,
    "user": { "_id": "default:users-frank-123" },
    "clientId": "jf93caauf3uzud7f308faesf3@provider.oada-dev.com"
  }
];

module.exports = [
  {
    "_key": "default:token-123",
    "token": "xyz",
    "scope": ["oada.rocks:all"],
    "createTime": 1413831649937,
    "expiresIn": 60,
    "user": { "_id": "default:users-frank-123" },
    "clientId": "jf93caauf3uzud7f308faesf3@provider.oada-dev.com"
  }
];

Add scope to token xyz.


'use strict';

var <%= classedName %> = {

}

<% if (es6) { %> export default <%= classedName %>; <% }
else { %>module.exports = <%= classedName %>; <% } %>

'use strict';

var <%= classedName %> = {

};

<% if (es6) { %> export default <%= classedName %>; <% }
else { %>module.exports = <%= classedName %>; <% } %>

Add missing semicolon to avoid warnings


/*global angular*/
(function () {
    'use strict';

    //Slideshows service used to communicate Slideshows REST endpoints
    angular.module('slideshows').factory('Slideshows', ['$resource',
        function ($resource) {
            return $resource('slideshows/:slideshowId',
                             { slideshowId: '@_id' },
                             { update:
                              {method: 'PUT'}
                             });
        }]);
}());

/*global angular*/
(function () {
    'use strict';

    //Slideshows service used to communicate Slideshows REST endpoints
    angular.module('slideshows').factory('Slideshows', ['$resource',
        function ($resource) {
            return $resource('slideshows/:slideshowId', {
                slideshowId: '@_id'
            }, {
                update: {
                    method: 'PUT'
                },
                getDevices: {
                    method: 'GET',
                    url: 'slideshowDevices/:slideshowId',
                    isArray: true,
                    params: {name: '@name'}
                },
                setDevices: {
                    method: 'PUT',
                    url: 'slideshowDevices/:slideshowId'
                }
            });
        }]);
}());

Add name as parameter when geting devices for a slideshow


import React from 'react'
import PropTypes from 'prop-types'

export default function HTML(props) {
    return (
        <html {...props.htmlAttributes}>
            <head>
                <meta charSet="utf-8" />
                <meta httpEquiv="x-ua-compatible" content="ie=edge" />
                <meta
                    name="viewport"
                    content="width=device-width, initial-scale=1, shrink-to-fit=no"
                />
                <link
                    rel="stylesheet"
                    href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css"
                />
                {props.headComponents}
            </head>
            <body {...props.bodyAttributes}>
                {props.preBodyComponents}
                <noscript key="noscript" id="gatsby-noscript">
                    This app works best with JavaScript enabled.
                </noscript>
                <div key={`body`} id="___gatsby" dangerouslySetInnerHTML={{ __html: props.body }} />
                {props.postBodyComponents}
            </body>
            <script
                type="text/javascript"
                src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"
            />
        </html>
    )
}

HTML.propTypes = {
    htmlAttributes: PropTypes.object,
    headComponents: PropTypes.array,
    bodyAttributes: PropTypes.object,
    preBodyComponents: PropTypes.array,
    body: PropTypes.string,
    postBodyComponents: PropTypes.array,
}

import React from 'react'
import PropTypes from 'prop-types'

export default function HTML(props) {
    return (
        <html {...props.htmlAttributes}>
            <head>
                <meta charSet="utf-8" />
                <meta httpEquiv="x-ua-compatible" content="ie=edge" />
                <meta
                    name="viewport"
                    content="width=device-width, initial-scale=1, shrink-to-fit=no"
                />
                {props.headComponents}
                <link
                    rel="stylesheet"
                    href="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.css"
                />
            </head>
            <body {...props.bodyAttributes}>
                {props.preBodyComponents}
                <noscript key="noscript" id="gatsby-noscript">
                    This app works best with JavaScript enabled.
                </noscript>
                <div key={`body`} id="___gatsby" dangerouslySetInnerHTML={{ __html: props.body }} />
                {props.postBodyComponents}
            </body>
            <script
                type="text/javascript"
                src="https://cdn.jsdelivr.net/npm/docsearch.js@2/dist/cdn/docsearch.min.js"
            />
        </html>
    )
}

HTML.propTypes = {
    htmlAttributes: PropTypes.object,
    headComponents: PropTypes.array,
    bodyAttributes: PropTypes.object,
    preBodyComponents: PropTypes.array,
    body: PropTypes.string,
    postBodyComponents: PropTypes.array,
}

Revert "Move DocSearch styles before headComponents"


'use strict';

module.exports = function(app) {
  app.controller('GamesController', ['$scope', 'UserService', 'FriendService', 'GameService', 'SearchService', function($scope, UserService, FriendService, GameService, SearchService) {

    let ctrl = this;
    $scope.FriendService = FriendService;
    $scope.allFriends = FriendService.data.allFriends;

    ctrl.user = UserService.data.user;
    ctrl.players = [];
    ctrl.creating = false;
    ctrl.editing = false;

    ctrl.createGame = function(gameData) {
      gameData.players = ctrl.players;
      GameService.createGame(gameData)
        .catch((err) => {
          console.log('Error creating game.');
        });
    };

    ctrl.addUserToGame = function(user) {
      ctrl.players.push(user);
    };

    ctrl.removePlayer = function(user) {
      let playersArray = ctrl.game.players;
      let userIndex = playersArray.indexOf(user);
      ctrl.friendsList.push(playersArray[userIndex]);
      playersArray.splice(userIndex, 1);
    };

    ctrl.initCreate = function() {
      let userData = {
        _id: ctrl.user._id,
        fullName: ctrl.user.fullName,
        email: ctrl.user.email,
      };
      FriendService.getAllFriends(ctrl.user.email);
      ctrl.players.push(userData);
      ctrl.creating = true;
    };

    ctrl.init = function() {
    };

    // ctrl.init();

  }]);
};

'use strict';

module.exports = function(app) {
  app.controller('GamesController', ['$scope', 'UserService', 'FriendService', 'GameService', 'SearchService', function($scope, UserService, FriendService, GameService, SearchService) {

    let ctrl = this;
    $scope.FriendService = FriendService;
    $scope.allFriends = FriendService.data.allFriends;

    ctrl.user = UserService.data.user;
    ctrl.players = [];
    ctrl.creating = false;
    ctrl.editing = false;

    ctrl.createGame = function(gameData) {
      gameData.players = ctrl.players;
      GameService.createGame(gameData);
    };

    ctrl.addUserToGame = function(user) {
      ctrl.players.push(user);
    };

    ctrl.removePlayer = function(user) {
      let playersArray = ctrl.game.players;
      let userIndex = playersArray.indexOf(user);
      ctrl.friendsList.push(playersArray[userIndex]);
      playersArray.splice(userIndex, 1);
    };

    ctrl.initCreate = function() {
      let userData = {
        _id: ctrl.user._id,
        fullName: ctrl.user.fullName,
        email: ctrl.user.email,
      };
      FriendService.getAllFriends(ctrl.user.email);
      ctrl.players.push(userData);
      ctrl.creating = true;
    };

    ctrl.init = function() {
    };

    // ctrl.init();

  }]);
};

Remove catch from createGame method. Errors are handled by service layer of app.


const webpack = require('webpack');
const helpers = require('./helpers');

module.exports = {
    entry: {
      'index': './src/index.js',
      // 'test': './src/test.js'
    },

    output: {
        path: helpers.root('dist'),
        filename: '[name].js',
        chunkFilename: '[id].chunk.js',
        libraryTarget: 'umd',
        library: 'Servable',
    },

    performance: {
        hints: false
    },

    resolve: {
        extensions: ['*', '.js'],
        modules: [helpers.root('node_modules')],
    },
    resolveLoader: {
        modules: [helpers.root('node_modules')]
    },

    module: {
        loaders: [
            {
                test: /\.js?$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [['es2015', {modules: false, loose: true}], 'stage-0'],
                    }
                },
                include: helpers.root('src')
            },
        ]
    },
};

const webpack = require('webpack');
const helpers = require('./helpers');

module.exports = {
    entry: {
      'index': './src/index.js',
      // 'test': './src/test.js'
    },

    output: {
        path: helpers.root('dist'),
        filename: '[name].js',
        chunkFilename: '[id].chunk.js',
        libraryTarget: 'umd',
        library: 'Servable',
        umdNamedDefine: true,
    },

    performance: {
        hints: false
    },

    resolve: {
        extensions: ['*', '.js'],
        modules: [helpers.root('node_modules')],
    },
    resolveLoader: {
        modules: [helpers.root('node_modules')]
    },

    module: {
        loaders: [
            {
                test: /\.js?$/,
                exclude: /(node_modules)/,
                use: {
                    loader: 'babel-loader',
                    options: {
                        presets: [['es2015', {modules: false, loose: true}], 'stage-0'],
                    }
                },
                include: helpers.root('src')
            },
        ]
    },
};

Update UMD Named define to true


exports.up = function(knex, Promise) {
  return Promise.all([
    knex.schema.createTable('next_users', table => {
      table.bigIncrements()
      table.string('email')
      table.string('name')
      table.string('password')
      table.string('origin')
      table.string('baseurl')
      table.string('phone')
      table.boolean('isVerified')
      table.string('verifyToken')
      table.string('verifyShortToken')
      table.bigint('verifyExpires')
      table.json('verifyChanges')
      table.string('resetToken')
      table.string('resetShortToken')
      table.bigint('resetExpires')
      table.timestamps()
      table.unique(['email'])
    })
  ])
}

exports.down = function(knex, Promise) {}

exports.up = function(knex, Promise) {
  return Promise.all([
    knex.schema.createTable('next_users', table => {
      table.bigIncrements()
      table.string('email')
      table.string('name')
      table.string('password')
      table.string('origin')
      table.string('baseurl')
      table.string('phone')
      table.boolean('isVerified')
      table.string('verifyToken')
      table.string('verifyShortToken')
      table.timestamp('verifyExpires')
      table.json('verifyChanges')
      table.string('resetToken')
      table.string('resetShortToken')
      table.timestamp('resetExpires')
      table.timestamps()
      table.unique(['email'])
    })
  ])
}

exports.down = function(knex, Promise) {}

Revert to using timestamps in database.


module.exports = {
  scenarios: [
    {
      name: 'default',
      dependencies: { }
    },
    {
      name: 'ember-release',
      dependencies: {
        'ember': 'components/ember#release'
      },
      resolutions: {
        'ember': 'release'
      }
    },
    {
      name: 'ember-beta',
      dependencies: {
        'ember': 'components/ember#beta'
      },
      resolutions: {
        'ember': 'beta'
      }
    },
    {
      name: 'ember-canary',
      dependencies: {
        'ember': 'components/ember#canary'
      },
      resolutions: {
        'ember': 'canary'
      }
    }
  ]
};

module.exports = {
  scenarios: [
    {
      name: 'default',
      dependencies: { }
    },
    {
      name: 'ember 1.13',
      dependencies: {
        'ember': '1.13.8'
      },
      resolutions: {
        'ember': '1.13.8'
      }
    },
    {
      name: 'ember-release',
      dependencies: {
        'ember': 'components/ember#release'
      },
      resolutions: {
        'ember': 'release'
      }
    },
    {
      name: 'ember-beta',
      dependencies: {
        'ember': 'components/ember#beta'
      },
      resolutions: {
        'ember': 'beta'
      }
    },
    {
      name: 'ember-canary',
      dependencies: {
        'ember': 'components/ember#canary'
      },
      resolutions: {
        'ember': 'canary'
      }
    }
  ]
};

Add 1.13 to the test matrix


'use strict';

const config = {};
/**
 * Heroku makes it available to help calculate correct concurrency
 * @see https://devcenter.heroku.com/articles/node-concurrency#tuning-the-concurrency-level
 */
config.memoryAvailable = parseInt(process.env.MEMORY_AVAILABLE, 10);
/**
 * Expected MAX memory footprint of a single concurrent process in Megabytes.
 *
 * NOTE: This value is the basis to calculate the Procfile server flag: --max_old_space_size=<size>
 *       The value in the Procfile is 90% of the estimated processMemory here.
 *       Based on a Heroku recommendation. @see https://blog.heroku.com/node-habits-2016#7-avoid-garbage
 */
config.processMemory = 200;
/**
 * Calculate total amount of concurrent processes to fork
 * based on available memory and estimated process memory footprint
 */
config.total = config.memoryAvailable ?
  Math.floor(config.memoryAvailable / config.processMemory) : 1;

module.exports = config;

'use strict';

const config = {};
/**
 * Heroku makes it available to help calculate correct concurrency
 * @see https://devcenter.heroku.com/articles/node-concurrency#tuning-the-concurrency-level
 */
config.memoryAvailable = parseInt(process.env.MEMORY_AVAILABLE, 10);
/**
 * Expected MAX memory footprint of a single concurrent process in Megabytes.
 *
 * NOTE: This value is the basis to calculate the Procfile server flag: --max_old_space_size=<size>
 *       The value in the Procfile is 90% of the estimated processMemory here.
 *       Based on a Heroku recommendation. @see https://blog.heroku.com/node-habits-2016#7-avoid-garbage
 */
config.processMemory = 170;
/**
 * Calculate total amount of concurrent processes to fork
 * based on available memory and estimated process memory footprint
 */
config.total = config.memoryAvailable ?
  Math.floor(config.memoryAvailable / config.processMemory) : 1;

module.exports = config;

Load test 170 per process


require("babel/register")({
  stage: 1,
  ignore: /node_modules/
});

global.Promise = require('bluebird')
global.Promise.onPossiblyUnhandledRejection((e) => { throw e; });

global.Promise.config({
  // Enable warnings.
  warnings: false,
  // Enable long stack traces.
  longStackTraces: true,
  // Enable cancellation.
  cancellation: true
});

GLOBAL.$redis = require('../config/database')
  , GLOBAL.$database = $redis.connect()
  , GLOBAL.$should = require('chai').should()
  , GLOBAL.$postgres = require('../config/postgres')
  , GLOBAL.$pg_database = $postgres.connect()

Add postgres connection and knex to global test variables.


/* Global Includes */
var testCase   = require('mocha').describe;
var pre        = require('mocha').before;
var preEach    = require('mocha').beforeEach;
var post       = require('mocha').after;
var postEach   = require('mocha').afterEach;
var assertions = require('mocha').it;
var assert     = require('chai').assert;
var validator  = require('validator');
var exec = require('child_process').execSync;
var artik      = require('../lib/artik-sdk');

/* Test Specific Includes */
var media = artik.media();
var sound_file = '/usr/share/sounds/alsa/Front_Center.wav';

/* Test Case Module */
testCase('Media', function() {

	pre(function() {
	});

	testCase('#play_sound_file', function() {

		assertions('Play the sound file', function() {
			media.play_sound_file(sound_file, function(response, status) {
				console.log('Finished playing');
			});
		});

	});

	post(function() {
	});

});

/* Global Includes */
var testCase   = require('mocha').describe;
var pre        = require('mocha').before;
var preEach    = require('mocha').beforeEach;
var post       = require('mocha').after;
var postEach   = require('mocha').afterEach;
var assertions = require('mocha').it;
var assert     = require('chai').assert;
var validator  = require('validator');
var exec = require('child_process').execSync;
var artik      = require('../lib/artik-sdk');

/* Test Specific Includes */
var media = artik.media();
var sound_file = '/usr/share/sounds/alsa/Front_Center.wav';
var start, end;

/* Test Case Module */
testCase('Media', function() {

	pre(function() {
	});

	testCase('#play_sound_file', function() {
		this.timeout(5000);
		start = new Date();

		assertions('Play the sound file', function(done) {

			media.play_sound_file(sound_file, function(response, status) {
				end = new Date();
				var timeOfPlay = (end.getTime() - start.getTime())/1000;
				console.log('Finished playing. Seconds ' + timeOfPlay);
				assert.isAtLeast(timeOfPlay, 1);
				done();
			});
		});

	});

	post(function() {
	});

});

Use time duration of test to measure the success


// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     <https://www.apache.org/licenses/LICENSE-2.0>
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const fs = require("fs");
const path = require("path");

module.exports = fs
  .readdirSync("src")
  .filter(filename => filename.endsWith(".js"))
  .map(filename => ({
    context: path.resolve("src"),
    entry: `./${filename}`,
    output: {
      filename,
      path: path.resolve("dist")
    },
    optimization: {
      minimize: false
    }
  }));

// Copyright 2018 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     <https://www.apache.org/licenses/LICENSE-2.0>
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

const fs = require("fs");
const path = require("path");
const webpack = require("webpack");

module.exports = fs
  .readdirSync("src")
  .filter(filename => filename.endsWith(".js"))
  .map(filename => ({
    context: path.resolve("src"),
    entry: `./${filename}`,
    output: {
      filename,
      path: path.resolve("dist")
    },
    optimization: {
      minimize: false
    },
    plugins: [
      new webpack.BannerPlugin({
        banner:
          "// Required for JavaScript engine shells.\n" +
          "if (typeof console === 'undefined') {\n" +
          "  console = {log: print};\n" +
          "}",
        raw: true
      })
    ]
  }));

Support older JavaScript engine shells.


/*
	This is just a proxy file requiring the component from the /addon folder and
	making it available to the dummy application!
 */
import SummerNoteComponent from 'ember-cli-bootstrap3-wysiwyg/components/bs-wysihtml5';

export default SummerNoteComponent;

/*
	This is just a proxy file requiring the component from the /addon folder and
	making it available to the dummy application!
 */
import SummerNoteComponent from 'ember-cli-summernote/components/summer-note';

export default SummerNoteComponent;

Fix the import path error.


$(function() {

    $('#side-menu').metisMenu();

});

//Loads the correct sidebar on window load,
//collapses the sidebar on window resize.
// Sets the min-height of #page-wrapper to window size
$(function() {
    $(window).bind("load resize", function() {
        topOffset = 50;
        width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width;
        if (width < 768) {
            $('div.navbar-collapse').addClass('collapse');
            topOffset = 100; // 2-row-menu
        } else {
            $('div.navbar-collapse').removeClass('collapse');
        }

        height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1;
        height = height - topOffset;
        if (height < 1) height = 1;
        if (height > topOffset) {
            $("#page-wrapper").css("min-height", (height) + "px");
        }
    });

    var url = window.location;
    var element = $('ul.nav a').filter(function() {
        return this.href == url;
    }).addClass('active').parent().parent().addClass('in').parent();
    if (element.is('li')) {
        element.addClass('active');
    }
});

$(function() {

    $('#side-menu').metisMenu();

});

//Loads the correct sidebar on window load,
//collapses the sidebar on window resize.
// Sets the min-height of #page-wrapper to window size
$(function() {
    $(window).bind("load resize", function() {
        topOffset = 50;
        width = (this.window.innerWidth > 0) ? this.window.innerWidth : this.screen.width;
        if (width < 768) {
            $('div.navbar-collapse').addClass('collapse');
            topOffset = 100; // 2-row-menu
        } else {
            $('div.navbar-collapse').removeClass('collapse');
        }

        height = ((this.window.innerHeight > 0) ? this.window.innerHeight : this.screen.height) - 1;
        height = height - topOffset;
        if (height < 1) height = 1;
        if (height > topOffset) {
            $("#page-wrapper").css("min-height", (height) + "px");
        }
    });

    var url = window.location;
    var element = $('ul.nav a').filter(function() {
        return this.href == url || url.href.indexOf(this.href) == 0;
    }).addClass('active').parent().parent().addClass('in').parent();
    if (element.is('li')) {
        element.addClass('active');
    }
});

Fix highlighting of menu points for sub-routes.


"use strict";

/*
  NOT a general purpose escaper! Only valid when *content* of a tag, not within an attribute.
 */
function escape(code) {
    return code
        .replace("&", "&", "g")
        .replace("<", "<", "g")
        .replace(">", ">", "g");
}

function lowlight(lang, lexer, code) {
    var ret = "";
    var lex = lexer.CeylonLexer(lexer.StringCharacterStream(code));
    var cur;
    while ((cur = lex.nextToken()) != null) {
        if (cur.type.string == "whitespace") {
            /*
               don’t put whitespace in a span –
               this way, styles can use CSS selectors
               based on the immediately previous node
               without worrying about whitespace
             */
            ret += cur.text;
        } else {
            ret += "";
            ret += escape(cur.text);
            ret += "";
        }
    }
    return ret;
}

"use strict";

/*
  NOT a general purpose escaper! Only valid when *content* of a tag, not within an attribute.
 */
function escape(code) {
    return code
        .replace("&", "&", "g")
        .replace("<", "<", "g")
        .replace(">", ">", "g");
}

function lowlight(lang, lexer, code) {
    var ret = "";
    var lex = lexer.CeylonLexer(lexer.StringCharacterStream(code));
    var cur;
    while ((cur = lex.nextToken()) != null) {
        if (cur.type.string == "whitespace") {
            /*
               don’t put whitespace in a span –
               this way, styles can use CSS selectors
               based on the immediately previous node
               without worrying about whitespace
             */
            ret += cur.text;
        } else {
            ret += "";
            ret += escaped;
            ret += "";
        }
    }
    return ret;
}

Add span content as content attribute


import { flashURLHandler } from './urlhandlers/flash_url_handler';
import { nodeURLHandler } from './urlhandlers/mock_node_url_handler';
import { XHRURLHandler } from './urlhandlers/xhr_url_handler';

function get(url, options, cb) {
  // Allow skip of the options param
  if (!cb) {
    if (typeof options === 'function') {
      cb = options;
    }
    options = {};
  }

  if (options.urlhandler && options.urlhandler.supported()) {
    // explicitly supply your own URLHandler object
    return options.urlhandler.get(url, options, cb);
  } else if (typeof window === 'undefined' || window === null) {
    return nodeURLHandler.get(url, options, cb);
  } else if (XHRURLHandler.supported()) {
    return XHRURLHandler.get(url, options, cb);
  } else if (flashURLHandler.supported()) {
    return flashURLHandler.get(url, options, cb);
  } else {
    return cb(
      new Error(
        'Current context is not supported by any of the default URLHandlers. Please provide a custom URLHandler'
      )
    );
  }
}

export const urlHandler = {
  get
};

import { flashURLHandler } from './urlhandlers/flash_url_handler';
import { nodeURLHandler } from './urlhandlers/mock_node_url_handler';
import { XHRURLHandler } from './urlhandlers/xhr_url_handler';

function get(url, options, cb) {
  // Allow skip of the options param
  if (!cb) {
    if (typeof options === 'function') {
      cb = options;
    }
    options = {};
  }

  if (typeof window === 'undefined' || window === null) {
    return nodeURLHandler.get(url, options, cb);
  } else if (XHRURLHandler.supported()) {
    return XHRURLHandler.get(url, options, cb);
  } else if (flashURLHandler.supported()) {
    return flashURLHandler.get(url, options, cb);
  } else {
    return cb(
      new Error(
        'Current context is not supported by any of the default URLHandlers. Please provide a custom URLHandler'
      )
    );
  }
}

export const urlHandler = {
  get
};

Remove useless check from default urlhandler


var webpack = require('webpack');

module.exports = {
  devtool: 'eval',
  entry: [
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    './scripts/index'
  ],
  output: {
    path: __dirname + '/scripts/',
    filename: 'bundle.js',
    publicPath: '/scripts/'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  module: {
    loaders: [
      { test: /\.jsx?$/, loaders: ['react-hot', 'babel'], exclude: /node_modules/ }
    ]
  }
};

var path = require('path');
var webpack = require('webpack');

module.exports = {
  devtool: 'eval',
  entry: [
    'webpack-dev-server/client?http://localhost:3000',
    'webpack/hot/only-dev-server',
    './scripts/index'
  ],
  output: {
    path: path.join(__dirname, 'build'),
    filename: 'bundle.js',
    publicPath: '/scripts/'
  },
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    new webpack.NoErrorsPlugin()
  ],
  resolve: {
    extensions: ['', '.js', '.jsx']
  },
  module: {
    loaders: [{
      test: /\.jsx?$/,
      loaders: ['react-hot', 'babel'],
      include: path.join(__dirname, 'scripts')
    }]
  }
};

Use include over exclude so npm link works


(function(stopwatch){
    var Game = stopwatch.Game = function(seconds){
        this.target = 1000 * seconds; // in milliseconds
    };
    Game.prototype.current = function(){
        return 0;
    };
    };

    var GameView = stopwatch.GameView = function(game, container){
        this.game = game;
        this.container = container;
        this.update();
    };
    GameView.prototype.update = function(){
        var target = this.container.querySelector('#target');
        target.innerHTML = this.game.target;
        var current = this.container.querySelector('#current');
        current.innerHTML = this.game.current();
    };
})(window.stopwatch = window.stopwatch || {});

(function(stopwatch){
    var Game = stopwatch.Game = function(seconds){
        this.target = 1000 * seconds; // in milliseconds
        this.started = false;
        this.stopped = false;
    };
    Game.prototype.current = function(){
        if (this.started) {
            return (this.stopped ? this.stopTime: this.time) - this.startTime;
        }
        return 0;
    };
    Game.prototype.start = function(){
        if (!this.started) {
            this.started = true;
            this.startTime = new Date().getTime();
            this.time = this.startTime;
        }
    };
    Game.prototype.stop = function(){
        if (!this.stopped) {
            this.stopped = true;
            this.stopTime = new Date().getTime();
        }
    };

    var GameView = stopwatch.GameView = function(game, container){
        this.game = game;
        this.container = container;
        this.update();
    };
    GameView.prototype.update = function(){
        var target = this.container.querySelector('#target');
        target.innerHTML = this.game.target;
        var current = this.container.querySelector('#current');
        current.innerHTML = this.game.current();
    };
})(window.stopwatch = window.stopwatch || {});

Create a start and stop method


#!/usr/bin/env node

const constMessage = "Hello, World!";
console.log(constMessage);

let letMessage = "Hello, World!";
console.log(letMessage);
letMessage = "Goodbye, World!";
console.log(letMessage);

const é = "Accented";
console.log(é);
const 学习 = "Learn";
console.log(学习);
// const ∃ = "There exists"; CRASH
// const 💹 = "Market"; CRASH
// const 😱 = "Eep!"; CRASH

Add weird variable names to let and const example


const { model: trashbinModel } = require('./db/trashbin.schema');

const createUserTrashbin = (userId) => {
	// access trashbin model
	const trashbin = trashbinModel({
		userId,
	});
	return trashbin.save();
};

const updateUserTrashbin = (userId, data = {}) => {
	// access trashbin model
	return trashbinModel.updateOne({ userId }, data, { upsert: true });
};

module.exports = {
	createUserTrashbin,
	updateUserTrashbin,
};

const { model: trashbinModel } = require('./db/trashbin.schema');

const createUserTrashbin = (userId) => {
	// access trashbin model
	const trashbin = trashbinModel({
		userId,
	});
	return trashbin.save();
};

const updateUserTrashbin = async (id, data = {}) => {
	// access trashbin model
	const trashbin = await trashbinModel.findById(id).exec();
	trashbin.set(data);
	return trashbin.save();
};

module.exports = {
	createUserTrashbin,
	updateUserTrashbin,
};

Use Mongoose functions for updating


import React from "react";
import { compose } from "react-apollo";
import hoistStatics from "hoist-non-react-statics";
import BaseProvider from "../components/BaseProvider";
import applyRedirect from "../router/applyRedirect";
import withCookies from "../cookies/withCookiesPage";
import withLocale from "../i18n/withLocalePage";

export default Page =>
  compose(withLocale, applyRedirect, withCookies)(
    hoistStatics(
      props => (
        <BaseProvider
          cookies={props.cookies}
          locale={props.locale}
          defaultLocale={props.defaultLocale}
          siteLocales={props.siteLocales}
        >
          <Page {...props} />
        </BaseProvider>
      ),
      Page
    )
  );

import React from "react";
import { compose } from "redux";
import hoistStatics from "hoist-non-react-statics";
import BaseProvider from "../components/BaseProvider";
import applyRedirect from "../router/applyRedirect";
import withCookies from "../cookies/withCookiesPage";
import withLocale from "../i18n/withLocalePage";

export default Page =>
  compose(withLocale, applyRedirect, withCookies)(
    hoistStatics(
      props => (
        <BaseProvider
          cookies={props.cookies}
          locale={props.locale}
          defaultLocale={props.defaultLocale}
          siteLocales={props.siteLocales}
        >
          <Page {...props} />
        </BaseProvider>
      ),
      Page
    )
  );

Replace react-apollo with redux compose


'use strict';

angular.module('houseBand')

.controller('NameCtrl', function($state){
  this.roomCheck = function(room){
    if(room){
      return $state.go('home', {room: room})
    }
    alert('Please name your band')
    return false
  }
});

'use strict';

angular.module('houseBand')

.controller('NameCtrl', function($state){
  this.roomCheck = function(room){
    if(room){
      return $state.go('home', {room: room.toLowerCase()})
    }
    alert('Please name your band');
    return false
  }
});

Make room name lower case


'use strict';

module.exports = (...trackers) => {
	const tracker = {};

	tracker.createEvent = (eventName, eventData) => {
		trackers.forEach((tk) => {
			tk.createEvent(eventName, eventData);
		});
		return tracker;
	};

	return tracker;
};

'use strict';

module.exports = function eventTracker() {
	const tracker = {};
	const trackers = Array.prototype.slice.call(arguments);

	tracker.createEvent = (eventName, eventData) => {
		trackers.forEach((tk) => {
			tk.createEvent(eventName, eventData);
		});
		return tracker;
	};

	return tracker;
};

Remove destructuring and add arguments


var mongoose  = require('mongoose')
  , elastical = require('elastical')
  , esClient  = new(require('elastical').Client)
  , should    = require('should')
  , config    = require('./config')
  , Schema    = mongoose.Schema
  , ObjectId  = Schema.ObjectId
  , mongoosastic = require('../lib/mongoosastic');

var TweetSchema = new Schema({
    user: String
  , post_date: {type:Date, es_type:'date'}
  , message: {type:String}
  , title: {type:String, es_boost:2.0}
});

TweetSchema.plugin(mongoosastic);
var BlogPost = mongoose.model('BlogPost', TweetSchema);

describe('Add Boost Option Per Field', function(){
  before(function(done){
    mongoose.connect(config.mongoUrl, function(){
      BlogPost.remove(function(){
        config.deleteIndexIfExists(['blogposts'], done)
      });
    });
  });

  it('should create a mapping with boost field added', function(done){
    BlogPost.createMapping(function(err, mapping){
      esClient.getMapping('blogposts', 'blogpost', function(err, mapping){
        var props = mapping.blogposts.mappings.blogpost.properties;
        props.title.type.should.eql('string');
        props.title.boost.should.eql(2.0);
        done();
      });
    });
  });
});

var mongoose  = require('mongoose')
  , elastical = require('elastical')
  , esClient  = new(require('elastical').Client)
  , should    = require('should')
  , config    = require('./config')
  , Schema    = mongoose.Schema
  , ObjectId  = Schema.ObjectId
  , mongoosastic = require('../lib/mongoosastic');

var TweetSchema = new Schema({
    user: String
  , post_date: {type:Date, es_type:'date'}
  , message: {type:String}
  , title: {type:String, es_boost:2.0}
});

TweetSchema.plugin(mongoosastic);
var BlogPost = mongoose.model('BlogPost', TweetSchema);

describe('Add Boost Option Per Field', function(){
  before(function(done){
    mongoose.connect(config.mongoUrl, function(){
      BlogPost.remove(function(){
        config.deleteIndexIfExists(['blogposts'], done)
      });
    });
  });

  it('should create a mapping with boost field added', function(done){
    BlogPost.createMapping(function(err, mapping){
      esClient.getMapping('blogposts', 'blogpost', function(err, mapping){
        /* elasticsearch 1.0 & 0.9 support */
        var props =
          mapping.blogpost != undefined ?
            mapping.blogpost.properties: /* ES 0.9.11 */
            mapping.blogposts.mappings.blogpost.properties; /* ES 1.0.0 */
        props.title.type.should.eql('string');
        props.title.boost.should.eql(2.0);
        done();
      });
    });
  });
});

Correct boost test field (support ES 0.9 and 1.0).


export const Locations = {
  OTHER: "Other",
  PRESET_LOCATIONS: [
    "Seattle, WA",
    "Redmond, WA",
    "Kirkland, WA",
    "Bellevue, WA",
    "Tacoma, WA",
    "Olympia, WA",
    "Bay Area, CA",
    "Baltimore, MD",
    "Other"
  ]
};

export const Locations = {
  OTHER: "Other",
  PRESET_LOCATIONS: [
    "Seattle, WA",
    "Redmond, WA",
    "Kirkland, WA",
    "Bellevue, WA",
    "Tacoma, WA",
    "Olympia, WA",
    "Portland, OR",
    "Bay Area, CA",
    "Baltimore, MD",
    "Other"
  ]
};

Add Portland, OR to location list


module.exports = {
  'Unit': require('./unit'),
  'Lexer': require('./lexer'),
};

module.exports = {
  // Objects
  'Unit': require('./unit'),
  'Lexer': require('./lexer'),

  // Methods
  'math': require('./math'),
};

Add math & clean up.


/*global Polymer, Cosmoz*/

(function () {

	'use strict';

	Polymer({

		is: 'cosmoz-bottom-bar-view',

		behaviors: [
			Cosmoz.ViewInfoBehavior,
			Polymer.IronResizableBehavior
		],

		properties: {

			overflowing: {
				type: Boolean,
				value: false,
				reflectToAttribute: true
			},

			scroller: {
				type: Object
			}
		},

		listeners: {
			'iron-resize': '_onResize'
		},

		attached: function () {
			this.scroller = this.$.scroller;
		},

		_onResize: function () {
			var scrollerSizer = this.$.scrollerSizer;

			// HACK(pasleq): ensure scrollerSizer is sized correctly.
			scrollerSizer.style.minHeight = '';
			this.async(function () {
				if (scrollerSizer.scrollHeight > scrollerSizer.offsetHeight) {
					scrollerSizer.style.minHeight = scrollerSizer.scrollHeight + 'px';
				}
			});

		},

		_getPadding: function (desktop) {
			// if (desktop) {
			// 	return;
			// }
			return 'padding-bottom: ' + this.$.bar.barHeight + 'px';
		},

		_getBarHeight: function (desktop) {
			return 'min-height: ' + this.$.bar.barHeight + 'px';
		}
	});
}());

/*global Polymer, Cosmoz*/

(function () {

	'use strict';

	Polymer({

		is: 'cosmoz-bottom-bar-view',

		behaviors: [
			Cosmoz.ViewInfoBehavior,
			Polymer.IronResizableBehavior
		],

		properties: {

			overflowing: {
				type: Boolean,
				value: false,
				reflectToAttribute: true
			},

			scroller: {
				type: Object
			}
		},

		listeners: {
			'iron-resize': '_onResize'
		},

		attached: function () {
			this.scroller = this.$.scroller;
		},

		_onResize: function () {
			var scrollerSizer = this.$.scrollerSizer;

			// HACK(pasleq): ensure scrollerSizer is sized correctly.
			scrollerSizer.style.minHeight = '';
			this.async(function () {
				if (scrollerSizer.scrollHeight > scrollerSizer.offsetHeight) {
					scrollerSizer.style.minHeight = scrollerSizer.scrollHeight + 'px';
				}
			});

		},

		_getPadding: function (desktop) {
			// if (desktop) {
			// 	return;
			// }
			return 'padding-bottom: ' + this.$.bar.barHeight + 'px';
		},

		_getBarHeight: function (desktop) {
			var height = this.$.bar.barHeight;
			return [
				'max-height: ' + height + 'px',
				'min-height: ' + height + 'px'
			].join(';');
		}
	});
}());

Fix bug where the placeholder would flex out


module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: './',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'app/**/*_test.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['Chrome', 'PhantomJS', 'Safari', 'Firefox'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true
  });
};

module.exports = function(config) {
  config.set({

    // base path that will be used to resolve all patterns (eg. files, exclude)
    basePath: './',

    // frameworks to use
    // available frameworks: https://npmjs.org/browse/keyword/karma-adapter
    frameworks: ['jasmine'],

    // list of files / patterns to load in the browser
    files: [
      'app/**/*_test.js'
    ],

    // list of files to exclude
    exclude: [
    ],

    // preprocess matching files before serving them to the browser
    // available preprocessors: https://npmjs.org/browse/keyword/karma-preprocessor
    preprocessors: {
    },

    // test results reporter to use
    // possible values: 'dots', 'progress'
    // available reporters: https://npmjs.org/browse/keyword/karma-reporter
    reporters: ['progress'],

    // web server port
    port: 9876,

    // enable / disable colors in the output (reporters and logs)
    colors: true,

    // level of logging
    // possible values: config.LOG_DISABLE || config.LOG_ERROR || config.LOG_WARN || config.LOG_INFO || config.LOG_DEBUG
    logLevel: config.LOG_INFO,

    // enable / disable watching file and executing tests whenever any file changes
    autoWatch: false,

    // start these browsers
    // available browser launchers: https://npmjs.org/browse/keyword/karma-launcher
    browsers: ['PhantomJS'],

    // Continuous Integration mode
    // if true, Karma captures browsers, runs the tests and exits
    singleRun: true
  });
};

Use Phantom JS alone for speed and ease.


export function getAbsoluteUrl({ path, getSassFileGlobPattern, glob }, url, includePaths = []) {
  const { dir, base } = path.parse(url);
  const baseGlobPattern = getSassFileGlobPattern(base);
  includePaths.some((includePath) => {
    glob.sync(path.resolve(includePath, dir, baseGlobPattern));
    return false;
  });
}

export default function getAbsoluteUrlFactory() {
  return getAbsoluteUrl.bind(null);
}

import globModule from 'glob';
import pathModule from 'path';

import getSassFileGlobPatternModule from './get-sass-file-glob-pattern';

export function getAbsoluteUrl({ path, getSassFileGlobPattern, glob }, url, includePaths = []) {
  const { dir, base } = path.parse(url);
  const baseGlobPattern = getSassFileGlobPattern(base);
  includePaths.some((includePath) => {
    glob.sync(path.resolve(includePath, dir, baseGlobPattern));
    return false;
  });
}

export default getAbsoluteUrl.bind(null, {
  path: pathModule,
  getSassFileGlobPattern: getSassFileGlobPatternModule,
  glob: globModule,
});

Implement the simplified testable module pattern.


'use strict';

var grunt = require('grunt');

/**
 * Constructs a test case.
 *
 * @param  {string}           file   The `package.json` file to be tested.
 * @param  {boolean}          valid  Flag indicating whether the test is
 *                                   expected to pass.
 * @param  {Array}            [args] Additional arguments to pass to `grunt`.
 * @return {function(Object)}         Testing function for `nodeunit`.
 */
function test(file, valid, args) {
    return function(test) {
        test.expect(2);

        grunt.util.spawn({
            grunt: true,
            args: [
                '--gruntfile', 'test/Gruntfile.js',
                '--pkgFile', file
            ].concat(args || []).concat('npm-validate')
        }, function(error, result, code) {
            if (valid) {
                test.equal(error, null);
                test.equal(code, 0);
            } else {
                test.notEqual(error, null);
                test.notEqual(code, 0);
            }
            test.done();
        });
    };
}

module.exports = {
    empty: test('fixtures/empty.json', false),
    force: test('fixtures/empty.json', true, ['--force', true]),
    minimal: test('fixtures/minimal.json', true),
    package: test('../package.json', true),
    strict: test('fixtures/minimal.json', false, ['--strict', true])
};

'use strict';

var grunt = require('grunt');

/**
 * Constructs a test case.
 *
 * @param  {string}           file   The `package.json` file to be tested.
 * @param  {boolean}          valid  Flag indicating whether the test is
 *                                   expected to pass.
 * @param  {Array}            [args] Additional arguments to pass to `grunt`.
 * @return {function(Object)}         Testing function for `nodeunit`.
 */
function test(file, valid, args) {
    return function(test) {
        test.expect(2);

        grunt.util.spawn({
            grunt: true,
            args: [
                '--gruntfile', 'test/Gruntfile.js',
                '--pkgFile', file
            ].concat(args || []).concat('npm-validate')
        }, function(error, result, code) {
            if (valid) {
                test.ifError(error);
                test.equal(code, 0);
            } else {
                test.notEqual(error, null);
                test.notEqual(code, 0);
            }
            test.done();
        });
    };
}

module.exports = {
    empty: test('fixtures/empty.json', false),
    force: test('fixtures/empty.json', true, ['--force', true]),
    minimal: test('fixtures/minimal.json', true),
    package: test('../package.json', true),
    strict: test('fixtures/minimal.json', false, ['--strict', true])
};

Use `ifError` assertion instead of `equal`.


/**
 * dubdrop compatible task for reading an image file and creating a thumbnail.
 *
 * @package thumbdrop
 * @author drk <drk@diy.org>
 */

// readAsDataURL

/**
 * Constructor
 */
function createThumbdrop (_callback) {
    return function thumbdrop (file, callback) {
        var reader = new FileReader();

        reader.onload = function (e) {
            var $img = document.createElement('img');
            $img.setAttribute('src', e.target.result);

            callback(null, file);
            _callback(null, $img)
        };

        reader.readAsDataURL(file);
    };
};

/**
 * Export
 */
module.exports = createThumbdrop;

/**
 * dubdrop compatible task for reading an image file and creating a thumbnail.
 *
 * @package thumbdrop
 * @author drk <drk@diy.org>
 */

// readAsDataURL

/**
 * Constructor
 */
function createThumbdrop (callback) {
    return function thumbdrop (file) {
        var reader = new FileReader();

        reader.onload = function (e) {
            var $img = document.createElement('img');
            $img.setAttribute('src', e.target.result);

            callback(null, $img)
        };

        reader.readAsDataURL(file);
    };
};

/**
 * Export
 */
module.exports = createThumbdrop;

Remove async handling to comply w/ dubdrop 0.0.3


'use strict'

let consistentEnv

module.exports = function() {
  if (!consistentEnv) {
    consistentEnv = require('consistent-env')
  }
  return consistentEnv().PATH || ''
}

module.exports.async = function() {
  if (!consistentEnv) {
    consistentEnv = require('consistent-env')
  }
  return consistentEnv.async().then(function(env) {
    return env.PATH || ''
  })
}

'use strict'

let consistentEnv

module.exports = function() {
  if (process.platform === 'win32') {
    return process.env.PATH || process.env.Path
  }
  if (!consistentEnv) {
    consistentEnv = require('consistent-env')
  }
  return consistentEnv().PATH || ''
}

module.exports.async = function() {
  if (process.platform === 'win32') {
    return Promise.resolve(process.env.PATH || process.env.Path)
  }
  if (!consistentEnv) {
    consistentEnv = require('consistent-env')
  }
  return consistentEnv.async().then(function(env) {
    return env.PATH || ''
  })
}

Handle different names of PATH on windows


'use strict';

var rump = module.exports = require('rump');
var configs = require('./configs');
var originalAddGulpTasks = rump.addGulpTasks;

rump.addGulpTasks = function(options) {
  originalAddGulpTasks(options);
  require('./gulp');
  return rump;
};

rump.on('update:main', function() {
  configs.rebuild();
  rump.emit('update:less');
});

Object.defineProperty(rump.configs, 'less', {
  get: function() {
    return configs.less;
  }
});

'use strict';

var rump = module.exports = require('rump');
var configs = require('./configs');
var originalAddGulpTasks = rump.addGulpTasks;

// TODO remove on next major core update
rump.addGulpTasks = function(options) {
  originalAddGulpTasks(options);
  require('./gulp');
  return rump;
};

rump.on('update:main', function() {
  configs.rebuild();
  rump.emit('update:less');
});

rump.on('gulp:main', function(options) {
  require('./gulp');
  rump.emit('gulp:less', options);
});

Object.defineProperty(rump.configs, 'less', {
  get: function() {
    return configs.less;
  }
});

Handle future event emit for gulp tasks


var mongoose = require('mongoose');
var ShortId = require('./shortid');

var defaultSave = mongoose.Model.prototype.save;
mongoose.Model.prototype.save = function(cb) {
  for (fieldName in this.schema.tree) {
    if (this.isNew && this[fieldName] === undefined) {
        var idType = this.schema.tree[fieldName];

        if (idType === ShortId || idType.type === ShortId) {
            var idInfo = this.schema.path(fieldName);
            var retries = idInfo.retries;
            var self = this;
            function attemptSave() {
                idInfo.generator(idInfo.generatorOptions, function(err, id) {
                    if (err) {
                        cb(err);
                        return;
                    }
                    self[fieldName] = id;
                    defaultSave.call(self, function(err, obj) {
                        if (err &&
                            err.code == 11000 &&
                            err.err.indexOf(fieldName) !== -1 &&
                            retries > 0
                        ) {
                            --retries;
                            attemptSave();
                        } else {
                            // TODO check these args
                            cb(err, obj);
                        }
                    });
                });
            }
            attemptSave();
            return;
        }
    }
  }
  defaultSave.call(this, cb);
};

module.exports = exports = ShortId;

var mongoose = require('mongoose');
var ShortId = require('./shortid');

var defaultSave = mongoose.Model.prototype.save;
mongoose.Model.prototype.save = function(cb) {
  for (key in this.schema.tree) {
    var fieldName = key
    if (this.isNew && this[fieldName] === undefined) {
        var idType = this.schema.tree[fieldName];

        if (idType === ShortId || idType.type === ShortId) {
            var idInfo = this.schema.path(fieldName);
            var retries = idInfo.retries;
            var self = this;
            function attemptSave() {
                idInfo.generator(idInfo.generatorOptions, function(err, id) {
                    if (err) {
                        cb(err);
                        return;
                    }
                    self[fieldName] = id;
                    defaultSave.call(self, function(err, obj) {
                        if (err &&
                            err.code == 11000 &&
                            err.err.indexOf(fieldName) !== -1 &&
                            retries > 0
                        ) {
                            --retries;
                            attemptSave();
                        } else {
                            // TODO check these args
                            cb(err, obj);
                        }
                    });
                });
            }
            attemptSave();
            return;
        }
    }
  }
  defaultSave.call(this, cb);
};

module.exports = exports = ShortId;

Create a variable in scope to avoid being override


var createJob = require('./createJob');

module.exports = function (sails) {
  return {
    jobs: {},

    defaults: {cron: {}},

    initialize: function (cb) {
      var config = sails.config.cron;
      var tasks = Object.keys(config);
      tasks.forEach(function (time) {
        this.jobs[time] = createJob({
          cronTime: time,
          onTick: config[time] instanceof Function ? config[time] : config[time].onTick,
          onComplete: config[time].onComplete,
          start: typeof config[time].start === 'boolean' ? config[time].start : true,
          timezone: config[time].timezone,
          context: config[time].context
        });
      }.bind(this));

      cb();
    }
  };
};

var createJob = require('./createJob');

module.exports = function (sails) {
  return {
    jobs: {},

    defaults: {cron: {}},

    initialize: function (cb) {
      var config = sails.config.cron;
      var tasks = Object.keys(config);
      tasks.forEach(function (name) {
        this.jobs[name] = createJob({
          cronTime: config[name].schedule,
          onTick: config[name].onTick,
          onComplete: config[name].onComplete,
          start: typeof config[name].start === 'boolean' ? config[name].start : true,
          timezone: config[name].timezone,
          context: config[name].context
        });
      }.bind(this));

      cb();
    }
  };
};

Replace schedule as a key with named job


module.exports = function(config) {
    config.set({
        frameworks: ['jasmine', 'browserify'],
        files: [
            'test/**/*.js'
        ],
        preprocessors: {
            'test/**/*.js': ['browserify']
        },
        browsers: ['PhantomJS'],
        browserify: {
            debug: true
        }
    });
};

module.exports = function(config) {
    config.set({
        frameworks: ['jasmine', 'browserify'],
        files: [
            'test/**/*.js'
        ],
        preprocessors: {
            'test/**/*.js': ['browserify']
        },
        browsers: ['PhantomJS'],
        browserify: {
            debug: true
        },
        singleRun: true
    });
};

Update karma, it will run once with npm test


module.exports = {
  waitForConditionPollInterval: 1000,
  waitForConditionTimeout: 6000,
  pauseDuration: 5000,
  retryAssertionTimeout: 2500,
}

module.exports = {
  waitForConditionPollInterval: 2000,
  waitForConditionTimeout: 12000,
  pauseDuration: 7500,
  retryAssertionTimeout: 5000,
}

Increase timeouts to improve test runs


'use strict';

// VARIABLES //

var FLOAT32_VIEW = new Float32Array( 1 );
var UINT32_VIEW = new Uint32Array( FLOAT32_VIEW.buffer );

// 0 11111111 00000000000000000000000 => 2139095040 => 0x7f800000
var PINF = 0x7f800000;

// Set the ArrayBuffer bit sequence:
UINT32_VIEW[ 0 ] = PINF;

// EXPORTS //

module.exports = FLOAT32_VIEW[ 0 ];

'use strict';

// VARIABLES //

var FLOAT32_VIEW = new Float32Array( 1 );
var UINT32_VIEW = new Uint32Array( FLOAT32_VIEW.buffer );

// 0 11111111 00000000000000000000000 => 2139095040 => 0x7f800000 (see IEEE 754-2008)
var PINF = 0x7f800000;

// Set the ArrayBuffer bit sequence:
UINT32_VIEW[ 0 ] = PINF;

// EXPORTS //

module.exports = FLOAT32_VIEW[ 0 ];

Add code comment re: IEEE 754-2008


import m from 'mithril';

const bigInputCard = {
    view(ctrl, args) {
        const cardClass = '.w-row.u-marginbottom-30.card.card-terciary.padding-redactor-description.text.optional.project_about_html.field_with_hint';

        return m(cardClass, [
            m('div', [
                m('label.field-label.fontweight-semibold.fontsize-base', args.label),
                (args.label_hint ? m('label.hint.fontsize-smallest.fontcolor-secondary', args.label_hint) : '')
            ]),
            m('div', args.children)
        ]);
    }
}

export default bigInputCard;

import m from 'mithril';

const bigInputCard = {
    view(ctrl, args) {
        const cardClass = args.cardClass || '.w-row.u-marginbottom-30.card.card-terciary.padding-redactor-description.text.optional.project_about_html.field_with_hint';

        return m(cardClass, {style: (args.cardStyle||{})}, [
            m('div', [
                m('label.field-label.fontweight-semibold.fontsize-base', args.label),
                (args.label_hint ? m('label.hint.fontsize-smallest.fontcolor-secondary', args.label_hint) : '')
            ]),
            m('div', args.children)
        ]);
    }
}

export default bigInputCard;

Adjust bigInputCard to receive a cardStyle and cardClass args


var dbm = require('db-migrate');
var type = dbm.dataType;

exports.up = function(db, callback) {
  db.createTable('bank_transactions', {
		id: { type: 'int', primaryKey: true, autoIncrement: true },
    deposit: { type: 'boolean', notNull: true },
    currency: { type: 'string', notNull: true },
    cashAmount: { type: 'decimal', notNull: true },
    accountId: { type: 'int', notNull: true },
    rippleTxId: { type: 'int'},
    createdAt: { type: 'datetime', notNull: true },
    updatedAt: { type: 'datetime' }
  }, callback);
};

exports.down = function(db, callback) {
  db.dropTable('bank_transactions', callback);
};

var dbm = require('db-migrate');
var type = dbm.dataType;

exports.up = function(db, callback) {
  db.createTable('external_transactions', {
		id: { type: 'int', primaryKey: true, autoIncrement: true },
    deposit: { type: 'boolean', notNull: true },
    currency: { type: 'string', notNull: true },
    cashAmount: { type: 'decimal', notNull: true },
    externalAccountId: { type: 'int', notNull: true },
    rippleTxId: { type: 'int'},
    createdAt: { type: 'datetime', notNull: true },
    updatedAt: { type: 'datetime' }
  }, callback);
};

exports.down = function(db, callback) {
  db.dropTable('bank_transactions', callback);
};

Change accountId to externalAccountId in external transactions migration


/*

HoC that provides a simple mutation that expects a single JSON object in return

Example usage:

export default withMutation({
  name: 'getEmbedlyData',
  args: {url: 'String'},
})(EmbedlyURL);

*/

import { graphql } from 'react-apollo';
import gql from 'graphql-tag';

export default function withMutation({name, args}) {

  const args1 = _.map(args, (type, name) => `$${name}: ${type}`); // e.g. $url: String
  const args2 = _.map(args, (type, name) => `${name}: $${name}`); // e.g. $url: url

  return graphql(gql`
    mutation ${name}(${args1}) {
      ${name}(${args2})
    }
  `, {
    props: ({ownProps, mutate}) => ({
      [name]: (vars) => {
        return mutate({
          variables: vars,
        });
      }
    }),
  });
}

/*

HoC that provides a simple mutation that expects a single JSON object in return

Example usage:

export default withMutation({
  name: 'getEmbedlyData',
  args: {url: 'String'},
})(EmbedlyURL);

*/

import { graphql } from 'react-apollo';
import gql from 'graphql-tag';

export default function withMutation({name, args}) {

  let mutation;

  if (args) {
    const args1 = _.map(args, (type, name) => `$${name}: ${type}`); // e.g. $url: String
    const args2 = _.map(args, (type, name) => `${name}: $${name}`); // e.g. $url: url
    mutation = `
      mutation ${name}(${args1}) {
        ${name}(${args2})
      }
    `
  } else {
    mutation = `
      mutation ${name} {
        ${name}
      }
    `
  }

  return graphql(gql`${mutation}`, {
    props: ({ownProps, mutate}) => ({
      [name]: (vars) => {
        return mutate({
          variables: vars,
        });
      }
    }),
  });
}

Make mutation hoc accept mutations without arguments


/* WePay API for Node.js
 * (c)2012 Matt Farmer
 * Release without warranty under the terms of the
 * Apache License. For more details, see the LICENSE
 * file at the root of this project.
*/
var UserInstance = function(params) {
  //TODO
}

module.exports = UserInstance;

/* WePay API for Node.js
 * (c)2012 Matt Farmer
 * Release without warranty under the terms of the
 * Apache License. For more details, see the LICENSE
 * file at the root of this project.
*/
var UserInstance = function(params) {
  // Populate any params passed in.
  if (params && ! params instanceof Object)
    throw "Parameters passed to an instance constructor must be an object or undefined.";

  if (params){
    for (key in params) {
      this[key] = params[key];
    }
  }
}

module.exports = UserInstance;

Implement the User instance constructor.


const gulp = require('gulp');
const replace = require('gulp-replace');

const Builder = require('jspm').Builder;

const conf = require('../conf/gulp.conf');

gulp.task('systemjs', systemjs);
gulp.task('systemjs:html', updateIndexHtml);

function systemjs(done) {
  const builder = new Builder('./', 'jspm.config.js');
  builder.config({
    paths: {
      "github:*": "jspm_packages/github/*",
      "npm:*": "jspm_packages/npm/*"
    },
    packageConfigPaths: [
      'npm:@*/*.json',
      'npm:*.json',
      'github:*/*.json'
    ]
  });
  builder.buildStatic(
    <%- entry %>,
    conf.path.tmp('index.js')
  ).then(() => done(), done);
}

function updateIndexHtml() {
  return gulp.src(conf.path.src('index.html'))
    .pipe(replace(
      /<script>\n\s*System.import.*\n\s*<\/script>/,
      `<script src="index.js"></script>`
    ))
    .pipe(gulp.dest(conf.path.tmp()));
}

const gulp = require('gulp');
const replace = require('gulp-replace');

const Builder = require('jspm').Builder;

const conf = require('../conf/gulp.conf');

gulp.task('systemjs', systemjs);
gulp.task('systemjs:html', updateIndexHtml);

function systemjs(done) {
  const builder = new Builder('./', 'jspm.config.js');
  builder.config({
    paths: {
      "github:*": "jspm_packages/github/*",
      "npm:*": "jspm_packages/npm/*"
    },
    packageConfigPaths: [
      'npm:@*/*.json',
      'npm:*.json',
      'github:*/*.json'
    ]
  });
  builder.buildStatic(
    <%- entry %>,
    conf.path.tmp('index.js')
  ).then(() => done(), done);
}

function updateIndexHtml() {
  return gulp.src(conf.path.src('index.html'))
    .pipe(replace(
      /<script src="jspm_packages\/system.js">[\s\S]*System.import.*\n\s*<\/script>/,
      `<script src="index.js"></script>`
    ))
    .pipe(gulp.dest(conf.path.tmp()));
}

Remove jspm scripts on build


const base = require('skatejs-build/karma.conf');
module.exports = function (config) {
  base(config);

  // Remove all explicit IE definitions.
  config.browsers = config.browsers.filter(name => !/^internet_explorer/.test(name));

  // Only test IE latest.
  config.browsers.push('internet_explorer_11');

  // Shims for testing.
  config.files = [
    'node_modules/es6-shim/es6-shim.js'
  ].concat(config.files);

  // Ensure mobile browsers have enough time to run.
  config.browserNoActivityTimeout = 60000;
};

const base = require('skatejs-build/karma.conf');
module.exports = function (config) {
  base(config);

  // Setup IE if testing in SauceLabs.
  if (config.sauceLabs) {
    // Remove all explicit IE definitions.
    config.browsers = config.browsers.filter(name => !/^internet_explorer/.test(name));

    // Only test IE latest.
    config.browsers.push('internet_explorer_11');
  }

  // Shims for testing.
  config.files = [
    'node_modules/es6-shim/es6-shim.js',
  ].concat(config.files);

  // Ensure mobile browsers have enough time to run.
  config.browserNoActivityTimeout = 60000;
};

Fix tests so that it only tries to run IE11 if running in Sauce Labs.


import $ from "cheerio";

const RECTANGULAR_SEQUENCE = ["M", "L", "L", "L", "L"];
const CIRCULAR_SEQUENCE = ["M", "m", "a", "a"];

const SvgTestHelper = {
  expectIsRectangular(wrapper) {
    expect(exhibitsShapeSequence(wrapper, RECTANGULAR_SEQUENCE)).to.be.true;
  },

  expectIsCircular(wrapper) {
    expect(exhibitsShapeSequence(wrapper, CIRCULAR_SEQUENCE)).to.be.true;
  }
};

function exhibitsShapeSequence(wrapper, shapeSequence) {
  const commands = getPathCommandsFromWrapper(wrapper);
  return commands.every((command, index) => {
    return command.name === shapeSequence[index];
  });
}

function getPathCommandsFromWrapper(wrapper) {
  const commandStr = $(wrapper.html()).attr("d");
  return parseSvgPathCommands(commandStr);
}

function parseSvgPathCommands(commandStr) {
  const matches = commandStr.match(
    /[MmLlHhVvCcSsQqTtAaZz]+[^MmLlHhVvCcSsQqTtAaZz]*/g
  );

  return matches.map(match => {
    const name = match.charAt(0);
    const args = match.substring(1).split(",").map(arg => parseFloat(arg, 10));

    return {
      raw: match,
      name,
      args
    }
  });
}

export default SvgTestHelper;

import $ from "cheerio";

const RECTANGULAR_SEQUENCE = ["M", "L", "L", "L", "L"];
const CIRCULAR_SEQUENCE = ["M", "m", "a", "a"];

const expectations = {
  expectIsRectangular(wrapper) {
    expect(exhibitsShapeSequence(wrapper, RECTANGULAR_SEQUENCE)).to.be.true;
  },

  expectIsCircular(wrapper) {
    expect(exhibitsShapeSequence(wrapper, CIRCULAR_SEQUENCE)).to.be.true;
  }
};

const helpers = {
  getBarHeight(wrapper) {
    expectations.expectIsRectangular(wrapper);
    const commands = getPathCommandsFromWrapper(wrapper);
    return Math.abs(commands[0].args[1] - commands[1].args[1]);
  }
};

const SvgTestHelper = Object.assign({}, expectations, helpers);

function exhibitsShapeSequence(wrapper, shapeSequence) {
  const commands = getPathCommandsFromWrapper(wrapper);
  return commands.every((command, index) => {
    return command.name === shapeSequence[index];
  });
}

function getPathCommandsFromWrapper(wrapper) {
  const commandStr = $(wrapper.html()).attr("d");
  return parseSvgPathCommands(commandStr);
}

function parseSvgPathCommands(commandStr) {
  const matches = commandStr.match(
    /[MmLlHhVvCcSsQqTtAaZz]+[^MmLlHhVvCcSsQqTtAaZz]*/g
  );

  return matches.map(match => {
    const name = match.charAt(0);
    const args = match.substring(1).split(",").map(arg => parseFloat(arg, 10));

    return {
      raw: match,
      name,
      args
    }
  });
}

export default SvgTestHelper;

Split helpers into expectations and helpers


import { describeComponent, it } from 'ember-mocha';
import { beforeEach, afterEach } from 'mocha';
import { expect } from 'chai';
import hbs from 'htmlbars-inline-precompile';
import page from './page';

describeComponent('ember-webcam', 'Integration: EmberWebcamComponent', {
  integration: true
}, () => {
  beforeEach(function () {
    page.setContext(this);
  });

  afterEach(() => {
    page.removeContext();
  });

  it('renders', () => {
    page.render(hbs`{{ember-webcam}}`);
    expect(page.viewer.isVisible).to.be.true;
  });

  it('takes a snapshot', done => {
    let isDone = false;
    page.context.setProperties({
      didError(errorMessage) {
        expect(errorMessage).to.be.ok;
        if (!isDone) {
          done();
          isDone = true;
        }
      }
    });
    page.render(hbs`
      {{#ember-webcam didError=(action didError) as |camera|}}
        <button {{action camera.snap}}>
          Take Snapshot!
        </button>
      {{/ember-webcam}}
    `);
    page.button.click();
  });
});

import { describeComponent, it } from 'ember-mocha';
import { beforeEach, afterEach } from 'mocha';
import { expect } from 'chai';
import hbs from 'htmlbars-inline-precompile';
import page from './page';

describeComponent('ember-webcam', 'Integration: EmberWebcamComponent', {
  integration: true
}, () => {
  beforeEach(function () {
    page.setContext(this);
  });

  afterEach(() => {
    page.removeContext();
  });

  it('renders', () => {
    page.render(hbs`{{ember-webcam}}`);
    expect(page.viewer.isVisible).to.be.true;
  });

  it('takes a snapshot', done => {
    let isDone = false;
    page.context.setProperties({
      didError(error) {
        // Should fail because camera is not available in test environment.
        expect(error.name).to.equal('WebcamError');
        if (!isDone) {
          done();
          isDone = true;
        }
      }
    });
    page.render(hbs`
      {{#ember-webcam didError=(action didError) as |camera|}}
        <button {{action camera.snap}}>
          Take Snapshot!
        </button>
      {{/ember-webcam}}
    `);
    page.button.click();
  });
});

Improve component test when fails to take snapshot


'use strict';

var assert = require('assert');

module.exports = function() {
  var privacy = {};

  var tags = { in: {}, out: {} };

  privacy.wrap = function(asset) {
    return {
      _unwrap: function(tag) {
        assert(tag === tags.in);

        return {
          tag: tags.out,
          asset: asset
        };
      }
    };
  };

  privacy.unwrap = function(wrappedAsset) {
    var unwrapped = wrappedAsset._unwrap(tags.in);
    assert(unwrapped.tag === tags.out);

    return unwrapped.asset;
  };

  return privacy;
};

Add actual code from jellyscript project


"use strict";

/**
 * Karma configuration.
 */

module.exports = function (config) {
  config.set({

    frameworks: ["mocha", "sinon"],

    files: [
      "test/**_test.js"
    ],

    preprocessors: {
      "test/**_test.js": ["webpack"]
    },

    reporters: ["progress"],

    browsers: ["Chrome"],

    webpack: require("./webpack.config"),

    plugins: [
      "karma-chrome-launcher",
      "karma-mocha",
      "karma-sinon",
      "karma-webpack"
    ]

  });
};

"use strict";

/**
 * Karma configuration.
 */

module.exports = function (config) {
  config.set({

    frameworks: ["mocha", "sinon"],

    files: [
      "test/**_test.js"
    ],

    preprocessors: {
      "test/**_test.js": ["webpack"]
    },

    reporters: ["progress"],

    browsers: ["Chrome"],

    webpack: require("./webpack.config"),

    plugins: [
      "karma-chrome-launcher",
      "karma-firefox-launcher",
      "karma-mocha",
      "karma-sinon",
      "karma-webpack"
    ]

  });
};

Add Karma Firefox Runner for TravisCI


var api = require('@request/api')

module.exports = (client, provider, methods, config, transform) => {
  return api(methods, {
    api: function (options, name) {
      options.api = name
      return this
    },
    auth: function (options, arg1, arg2) {
      var alias = (options.api || provider.api || '__default')
      config.endpoint.auth(alias, options, arg1, arg2)
      return this
    },
    submit: function (options) {
      var alias = (options.api || provider.api || '__default')

      if (!config.aliases[alias]) {
        throw new Error('Purest: non existing alias!')
      }

      transform.oauth(options)
      transform.parse(options)
      options = config.endpoint.options(alias, options)

      if (!/^http/.test(options.url)) {
        options.url = config.url(alias, options)
      }

      return client(options)
    }
  })
}

var api = require('@request/api')

module.exports = (client, provider, methods, config, transform) => {
  return api(methods, {
    api: function (name) {
      this._options.api = name
      return this
    },
    auth: function (arg1, arg2) {
      var alias = (this._options.api || provider.api || '__default')
      config.endpoint.auth(alias, this._options, arg1, arg2)
      return this
    },
    submit: function (callback) {
      var options = this._options

      if (callback) {
        options.callback = callback
      }

      var alias = (options.api || provider.api || '__default')

      if (!config.aliases[alias]) {
        throw new Error('Purest: non existing alias!')
      }

      transform.oauth(options)
      transform.parse(options)
      options = config.endpoint.options(alias, options)

      if (!/^http/.test(options.url)) {
        options.url = config.url(alias, options)
      }

      return client(options)
    }
  })
}

Fix custom method options + Allow callback to be passed to the submit method


var Person = require('./person');

function incomingMessage(botId, message) {
  return message.type === 'message' // check it's a message
          && Boolean(message.text) // check message has some content
          && Boolean(message.user) // check there is a user set
          && message.user !== botId; // check message is not from this bot
}

function directMessage(message) {
  // check it's a direct message - direct message channels start with 'D'
  return typeof message.channel === 'string' && message.channel[0] === 'D';
}

function channelMessageForMe(botId, message) {
  return typeof message.channel === 'string' // check it's a string
          && message.channel[0] === 'C' // channel messages start with 'C'
          && message.text.startsWith('<@' + botId + '>:'); // check it's for me
}

function createResponse(message, callback) {

  var response = 'Echo: ' + message.text;
  if (message.text.startsWith('person')) {
    var person = new Person(message.text.substring(7));
    person.getDetails(callback);
  }

  callback(response);

}

module.exports = {
  incomingMessage: incomingMessage,
  directMessage: directMessage,
  channelMessageForMe: channelMessageForMe,
  createResponse: createResponse
};

var Person = require('./person');

function incomingMessage(botId, message) {
  return message.type === 'message' // check it's a message
          && Boolean(message.text) // check message has some content
          && Boolean(message.user) // check there is a user set
          && message.user !== botId; // check message is not from this bot
}

function directMessage(message) {
  // check it's a direct message - direct message channels start with 'D'
  return typeof message.channel === 'string' && message.channel[0] === 'D';
}

function channelMessageForMe(botId, message) {
  return typeof message.channel === 'string' // check it's a string
          && message.channel[0] === 'C' // channel messages start with 'C'
          && message.text.startsWith('<@' + botId + '>:'); // check it's for me
}

function createResponse(message, callback) {

  if (message.text.startsWith('person')) {
    var person = new Person(message.text.substring(7));
    person.getDetails(callback);
  } else {
    // if nothing else, just echo back the message
    callback('Echo: ' + message.text);
  }

}

module.exports = {
  incomingMessage: incomingMessage,
  directMessage: directMessage,
  channelMessageForMe: channelMessageForMe,
  createResponse: createResponse
};

Stop double-calling the callback function


import Vue from 'vue'
import axios from '@/services/axios'

export default {
  namespaced: true,
  state: {
    token: null,
    tokens: {},
  },
  getters: {
    token: state => state.token,
    tokenExists: state => token => Boolean(state.tokens[token]),
  },
  actions: {
    updateToken ({ commit, rootGetters }, token) {
      commit('setToken', token)
    },
    async fetchTokens ({ commit }) {
      commit('receiveTokens', (await axios.get('/api/subscriptions/push/')).data.map(({ token }) => token))
    },
  },
  mutations: {
    setToken (state, token) {
      state.token = token
    },
    receiveTokens (state, tokens) {
      for (let token of tokens) {
        Vue.set(state.tokens, token, true)
      }
    },
  },
}

export const plugin = store => {
  store.watch(() => ({
    isLoggedIn: store.getters['auth/isLoggedIn'],
    token: store.getters['fcm/token'],
  }), async ({ isLoggedIn, token }) => {
    if (isLoggedIn && token) {
      await store.dispatch('fcm/fetchTokens')
      if (!store.getters['fcm/tokenExists'](token)) {
        await axios.post('/api/subscriptions/push/', { token, platform: 'android' })
        await store.dispatch('fcm/fetchTokens')
      }
    }
  })
}

import Vue from 'vue'
import axios from '@/services/axios'

export default {
  namespaced: true,
  state: {
    token: null,
    tokens: {},
  },
  getters: {
    token: state => state.token,
    tokenExists: state => token => Boolean(state.tokens[token]),
  },
  actions: {
    updateToken ({ commit, rootGetters }, token) {
      commit('setToken', token)
    },
    async fetchTokens ({ commit }) {
      commit('receiveTokens', (await axios.get('/api/subscriptions/push/')).data.map(({ token }) => token))
    },
  },
  mutations: {
    setToken (state, token) {
      state.token = token
    },
    receiveTokens (state, tokens) {
      state.tokens = {}
      for (let token of tokens) {
        Vue.set(state.tokens, token, true)
      }
    },
  },
}

export const plugin = store => {
  store.watch(() => ({
    isLoggedIn: store.getters['auth/isLoggedIn'],
    token: store.getters['fcm/token'],
  }), async ({ isLoggedIn, token }) => {
    if (isLoggedIn && token) {
      await store.dispatch('fcm/fetchTokens')
      if (!store.getters['fcm/tokenExists'](token)) {
        await axios.post('/api/subscriptions/push/', { token, platform: 'android' })
        await store.dispatch('fcm/fetchTokens')
      }
    }
  })
}

Clear subscription tokens before updating


"use strict";

;(function ( root, name, definition ) {
    /*
     * Exports
     */
    if ( typeof define === 'function' && define.amd ) {
        define( [], definition );
    }
    else if ( typeof module === 'object' && module.exports ) {
        module.exports = definition();
    }
    else {
        root[ name ] = definition();
    }
})( this, 'crouch', function () {
    /**
     * RegExp to find placeholders in the template
     *
     * http://regexr.com/3e1o7
     * @type {RegExp}
     */
    var _re = /{([0-9a-zA-Z]+?)}/g;

    /**
     * Micro template compiler
     *
     * @param {string} template
     * @param {array|object} values
     * @returns {string}
     */
    var crouch = function ( template, values ) {
        /*
         * Default arguments
         */
        var
            template = template || "",
            values   = values || {};

        var match;

        /*
         * Loop through all the placeholders that matched with regex
         */
        while ( match = _re.exec( template ) ) {
            /*
             * Get value from given values and
             * if it doesn't exist use empty string
             */
            var _value = values[ match[ 1 ] ];
            if ( !_value ) _value = "";

            /*
             * Replace the placeholder with a real value.
             */
            template = template.replace( match[ 0 ], _value )
        }

        /*
         * Return the template with filled in values
         */
        return template;
    };

    /**
     * 😎
     */
    return crouch;
} );

"use strict";

;(function ( root, name, definition ) {
    /*
     * Exports
     */
    if ( typeof define === 'function' && define.amd ) {
        define( [], definition );
    }
    else if ( typeof module === 'object' && module.exports ) {
        module.exports = definition();
    }
    else {
        root[ name ] = definition();
    }
})( this, 'crouch', function () {
    /**
     * RegExp to find placeholders in the template
     *
     * http://regexr.com/3eveu
     * @type {RegExp}
     */
    var _re = /{([0-9a-z_]+?)}/ig;

    /**
     * Micro template compiler
     *
     * @param {string} template
     * @param {array|object} values
     * @returns {string}
     */
    var crouch = function ( template, values ) {
        /*
         * Default arguments
         */
        var
            template = template || "",
            values   = values || {};

        var match;

        /*
         * Loop through all the placeholders that matched with regex
         */
        while ( match = _re.exec( template ) ) {
            /*
             * Get value from given values and
             * if it doesn't exist use empty string
             */
            var _value = values[ match[ 1 ] ];
            if ( !_value ) _value = "";

            /*
             * Replace the placeholder with a real value.
             */
            template = template.replace( match[ 0 ], _value )
        }

        /*
         * Return the template with filled in values
         */
        return template;
    };

    /**
     * 😎
     */
    return crouch;
} );

Update regex to match underscore and be case insensitive;


// Includes
var http = require('./http.js').func;
var getVerification = require('./getVerification.js').func;
var promise = require('./promise.js');

// Args
exports.required = ['url', 'events'];
exports.optional = ['customOpt', 'getBody', 'jar'];

function general (jar, url, inputs, events, customOpt, body) {
  return function (resolve, reject) {
    for (var input in events) {
      inputs[input] = events[input];
    }
    var httpOpt = {
      url: url,
      options: {
        resolveWithFullResponse: true,
        method: 'POST',
        form: inputs,
        jar: jar
      }
    };
    if (customOpt) {
      Object.assign(httpOpt.options, customOpt);
    }
    http(httpOpt).then(function (res) {
      resolve({
        res: res,
        body: body
      });
    });
  };
}

exports.func = function (args) {
  var jar = args.jar;
  var url = args.url;
  return getVerification({url: url, jar: jar, getBody: args.getBody})
  .then(function (response) {
    return promise(general(jar, url, response.inputs, args.events, args.http, response.body));
  });
};

// Includes
var http = require('./http.js').func;
var getVerification = require('./getVerification.js').func;
var promise = require('./promise.js');

// Args
exports.required = ['url', 'events'];
exports.optional = ['http', 'ignoreCache', 'getBody', 'jar'];

function general (jar, url, inputs, events, customOpt, body) {
  return function (resolve, reject) {
    for (var input in events) {
      inputs[input] = events[input];
    }
    var httpOpt = {
      url: url,
      options: {
        resolveWithFullResponse: true,
        method: 'POST',
        form: inputs,
        jar: jar
      }
    };
    if (customOpt) {
      if (customOpt.url) {
        delete customOpt.url;
      }
      Object.assign(httpOpt.options, customOpt);
    }
    http(httpOpt).then(function (res) {
      resolve({
        res: res,
        body: body
      });
    });
  };
}

exports.func = function (args) {
  var jar = args.jar;
  var url = args.url;
  var custom = args.http;
  return getVerification({url: custom ? (custom.url || url) : url, jar: jar, ignoreCache: args.ignoreCache, getBody: args.getBody})
  .then(function (response) {
    return promise(general(jar, url, response.inputs, args.events, args.http, response.body));
  });
};

Add ignoreCache option and http option


'use babel'

import {BufferedProcess} from 'atom'

export function exec(command, args = [], options = {}) {
  if (!arguments.length) throw new Error('Nothing to execute')

  return new Promise(function(resolve, reject) {
    const data = {stdout: [], stderr: []}
    const spawnedProcess = new BufferedProcess({
      command: command,
      args: args,
      options: options,
      stdout: function(contents) {
        data.stdout.push(contents)
      },
      stderr: function(contents) {
        data.stderr.push(contents)
      },
      exit: function() {
        if (data.stderr.length) {
          reject(new Error(data.stderr.join('')))
        } else {
          resolve(data.stdout.join(''))
        }
      }
    })
    spawnedProcess.onWillThrowError(function({error, handle}) {
      reject(error)
      handle()
    })
  })
}

'use babel'

import {BufferedProcess} from 'atom'

export function installPackages(packageNames, callback) {
  const APMPath = atom.packages.getApmPath()
  const Promises = []

  return Promise.all(Promise)
}
export function exec(command, args = [], options = {}) {
  if (!arguments.length) throw new Error('Nothing to execute')

  return new Promise(function(resolve, reject) {
    const data = {stdout: [], stderr: []}
    const spawnedProcess = new BufferedProcess({
      command: command,
      args: args,
      options: options,
      stdout: function(contents) {
        data.stdout.push(contents)
      },
      stderr: function(contents) {
        data.stderr.push(contents)
      },
      exit: function() {
        if (data.stderr.length) {
          reject(new Error(data.stderr.join('')))
        } else {
          resolve(data.stdout.join(''))
        }
      }
    })
    spawnedProcess.onWillThrowError(function({error, handle}) {
      reject(error)
      handle()
    })
  })
}

Create a dummy installPackages function


var gulp = require('gulp');
var browserSync = require('browser-sync');
var vulcanize = require('gulp-vulcanize');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');

gulp.task('copy', function () {
  return gulp.src('./app/index.html', {base: './app/'})
    .pipe(gulp.dest('./dist/'));
});

gulp.task('compress', function () {
  return gulp.src('./app/bower_components/webcomponentsjs/webcomponents-lite.min.js')
    .pipe(uglify({mangle: true}))
    .pipe(concat('_polymer.js'))
    .pipe(gulp.dest('./dist/'));
});

gulp.task('vulcanize', function () {
  return gulp.src('app/elements/my-app.html')
    .pipe(vulcanize({
      stripComments: true,
      inlineCss: true,
      inlineScripts: true
    }))
    .pipe(concat('_polymer.html'))
    .pipe(gulp.dest('./dist/'));
});

gulp.task('build', ['copy', 'compress', 'vulcanize']);

gulp.task('serve', ['build'], function () {
  browserSync.init({
    server: './dist'
  });

  gulp.watch('app/elements/**/*.html', ['vulcanize']);
  gulp.watch('app/index.html', ['copy']);
  gulp.watch('dist/*.html').on('change', browserSync.reload);
});

var gulp = require('gulp');
var browserSync = require('browser-sync');
var vulcanize = require('gulp-vulcanize');
var uglify = require('gulp-uglify');
var concat = require('gulp-concat');

gulp.task('copy', function () {
  return gulp.src('./app/index.html', {base: './app/'})
    .pipe(gulp.dest('./dist/'));
});

gulp.task('compress', function () {
  return gulp.src('./app/bower_components/webcomponentsjs/webcomponents-lite.min.js')
    .pipe(uglify({mangle: true}))
    .pipe(concat('_polymer.js'))
    .pipe(gulp.dest('./dist/'));
});

gulp.task('vulcanize', function () {
  return gulp.src('app/elements/my-app.html')
    .pipe(vulcanize({
      stripComments: true,
      inlineCss: true,
      inlineScripts: true
    }))
    .pipe(concat('_polymer.html'))
    .pipe(gulp.dest('./dist/'));
});

gulp.task('build', ['copy', 'compress', 'vulcanize']);

gulp.task('serve', ['build'], function () {
  browserSync.init({
    server: './dist',
    port: 4200
  });

  gulp.watch('app/elements/**/*.html', ['vulcanize']);
  gulp.watch('app/index.html', ['copy']);
  gulp.watch('dist/*.html').on('change', browserSync.reload);
});

Change browserSync to use port 4200