<commit_before>/**
 * Layer is a 1:1 mapping to a dataset. It has a set
 * schema. On creation, it opens a temp file
 * and will flush features (SoQLValues) to it as they're written
 * to the layer.
 */
import _ from 'underscore';
import fs from 'fs';
import es from 'event-stream';
import uuid from 'uuid';
import {
  EventEmitter
}
from 'events';
import ldj from 'ldjson-stream';
import srs from 'node-srs';
import BBox from '../util/bbox';
import {
  Duplex
}
from 'stream';
import {
  types
}
from '../soql/mapper';
import logger from '../util/logger';
import conf from '../config';

var config = conf();
const scratchPrologue = "";
const scratchSeparator = "\n";
const scratchEpilogue = "";

const jsonPrologue = "[";
const jsonSeparator = ",";
const jsonEpilogue = "]";

const WGS84 = '+proj=longlat +ellps=WGS84 +no_defs';

class Layer extends Duplex {
  static get EMPTY() {
    return '__empty__';
  }

  constructor(columns, position, disk, spec) {
    if (position === undefined) throw new Error("Need a layer index!");
    super();
    this._position = position;

    this.columns = columns;
    this._count = 0;
    this._crsMap = {};
    this._projectTo = srs.parse(WGS84);
    this._spec = spec || {};

    this._bbox = new BBox();

    if (disk) {
      this._outName = '/tmp/import_' + uuid.v4() + '.ldjson';
      this._out = disk.allocate(this._outName);
      this._out.write(scratchPrologue);
    }
  }

  toJSON() {
    return {
      count: this.count,
      projection: this.defaultCrs.name,
      name: this.name,
      geometry: this.geomType,
      bbox: this._bbox.toJSON(),
      columns: this.columns.map((c) => c.toJSON())
    };
  }

  get scratchName() {
    return this._outName;
  }

  get name() {
    return this._spec.name || `layer_${this._position}`;
  }

  get uid() {
    return this._spec.uid || Layer.EMPTY;
  }

  set uid(uid) {
    this._spec.uid = uid;
    return this;
  }

  belongsIn(soqlRow) {
    for (var i = 0; i < this.columns.length; i++) {
      let column = this.columns[i];
      let soqlValue = soqlRow[i];

      let nameMatch = column.rawName === soqlValue.rawName;
      if (!nameMatch) return false;

      let typeMatch = column.ctype === soqlValue.ctype;
      let isEmpty = (column.ctype === 'null' || soqlValue.ctype === 'null');
      if (!typeMatch && !isEmpty) return false;
    }
    return true;
  }

  get geomType() {
    var geom = this.columns.find((col) => col.isGeometry);
    if (!geom) return null;
    return geom.dataTypeName;
  }

  set defaultCrs(urn) {
    this._defaultCrs = srs.parse(urn);
  }

  get defaultCrs() {
    return this._defaultCrs || this._projectTo;
  }

  get projections() {
    return Object.values(this._crsMap);
  }

  /**
   * Layers are created on the fly. They are created when we encounter the
   * a soqlRow which does not fit in the set of layers that we know about.
   * _updateColumnTypes mutates the Layer's column types as values that
   * have more type info are written to it.
   *
   * An Example:
   *   A dataset is being read which looks like
   *   [
   *     ['a', 1, POINT(1, 2), null],
   *     ['b', 1, POINT(2, 3), 'something']
   *     ['c', 1, POINT(3, 4), null]
   *   ]
   *
   *
   *   When we encounter the first row, a Layer, layer0, is created with the first soqlRow:
   *     ['a', 1, POINT(1, 2), null]
   *   So we end up with layer0 that has columns of type
   *     [SoQLText, SoQLNumber, SoQLPoint, SoQLNull]
   *
   *   Then we encounter the second row, row1, which has more info about what the last
   *   column actually is.
   *     ['b', 1, POINT(2, 3), 'something']
   *   layer0.belongsIn(row1) will return true, so row1 will get written to layer0.
   *   layer0.write will call _updateColumnTypes to update its undefined columns to reflect
   *   the fact that it is now storing values with more type info than it had before.
   *
   * This is a little confusing and gross, and comes from the fact that this is
   * being written to the lowest common denominator (geoJSON, KML, KMZ) of type
   * info. ESRI shapefiles give more hints about types, though all those hints
   * are eliminated in the shapefile library being used.
   *
   * This could be written as a big map, but it is called on every write,
   * so it's important to return as early as possible.
   *
   * @mutates this.columns
   */
  _updateColumnTypes(soqlRow) {
    var undefinedColumns = this.columns.filter((c) => c.ctype === 'null');
    if (!undefinedColumns.length) return;

    var definedSoqlCols = soqlRow.filter((c) => c.ctype !== 'null');
    //We want the set of columns that are defined for the value, but are not defined
    //for the layer. These are the columns that we will replace in the layer.
    var toAdd = definedSoqlCols.filter((valCol) => {
      return undefinedColumns.find((col) => valCol.rawName === col.rawName);
    });

    if (toAdd.length === 0) return;

    this.columns = this.columns.map((col) => {
      var valCol = toAdd.find((newCol) => newCol.rawName === col.rawName);
      var newCol;
      if (valCol) {
        newCol = new valCol.constructor(valCol.rawName);
        logger.debug(`Replacing old undefined column ${valCol.rawName} with new ${valCol.constructor.name} column`);
      }
      return newCol || col;
    });
  }

  /**
   * Write the soqlRow with a given crs to the layer.
   * This will only be called if belongsIn returned true for the soqlRow and this layer.
   * See the note above on the confusing mutate-y  bits.
   * @param  {[type]} crs       [description]
   * @param  {[type]} soqlRow   [description]
   * @param  {[type]} throwaway [description]
   * @return {[type]}           [description]
   */
  write(crs, soqlRow, throwaway, done) {
    if (crs) this._crsMap[this._count] = crs;

    this._updateColumnTypes(soqlRow);
    this._count++;
    if (throwaway) {
      if (done) done();
      return;
    }

    this._out.write(
      this._scratchSeparator(this._count) + JSON.stringify(soqlRow.map((r) => r.value)) + '\n',
      null, done);
  }

  _scratchSeparator(index) {
    return index === 0 ? '' : scratchSeparator;
  }

  _jsonSeparator(index) {
    return index === 0 ? '' : jsonSeparator;
  }

  close(cb) {
    this._out.write(scratchEpilogue, null, () => {
      this._out.on('finish', () => {
        this.emit('readable');
        //why is this not being set?
        this._readableState.emittedReadable = true;
        cb();
      });
      this._out.end();
    });
  }

  get count() {
    return this._count;
  }

  _expandBbox(geom) {
    //yes a map is silly here because this is a side effect on the layer
    //but it keeps things simple
    geom.mapCoordinates((coord) => {
      this._bbox.expand(coord);
    });
  }

  _getProjectionFor(index) {
    if (!this._crsMap[index]) return this.defaultCrs;
    return srs.parse(this._crsMap[index]);
  }

  _startPushing() {
    this._isPushing = true;
    var projectTo = this._projectTo;
    var index = 0;

    this.push(jsonPrologue);
    fs.createReadStream(this._outName)
      .pipe(ldj.parse())
      .pipe(es.map((row, cb) => {
        var soqlRow = _.zip(this.columns, row).map(([column, value]) => {
          var soql = new types[column.ctype](column.rawName, value);
          if (soql.isGeometry) {
            let projection = this._getProjectionFor(index);
            let geom = soql.reproject(projection, projectTo);
            this._expandBbox(geom);
            return geom;
          }
          return soql;
        });

        var sep = this._jsonSeparator(index);
        var ep = '';
        index++;
        if (index === this._count) ep = jsonEpilogue;

        var asSoda = soqlRow.reduce((acc, col) => {
          acc[col.name] = col.value;
          return acc;
        }, {});

        var rowString = sep + JSON.stringify(asSoda) + ep;
        this.push(rowString);
        cb();
      })).on('end', () => {
        this.push(null);
      });
  }

  /**
    Read the rows back out from the scratch file that was written
    as their SoQL reps, and reproject them into WGS84

    @sideeffect: update the layer's _bbox property after the geometry
                 is reprojected. this expands the bbox if the geometry
                 lies outside of the bbox. After all features have been
                 read then _bbox will encompass all of them
  */
  _read() {
    if (!this._readableState.emittedReadable && !this._isPushing) {
      this.once('readable', this._startPushing.bind(this));
    } else if (!this._isPushing) {
      this._startPushing();
    }
  }
}

export default Layer;<commit_msg>remove use of internal emittedReadable flag<commit_after>/**
 * Layer is a 1:1 mapping to a dataset. It has a set
 * schema. On creation, it opens a temp file
 * and will flush features (SoQLValues) to it as they're written
 * to the layer.
 */
import _ from 'underscore';
import fs from 'fs';
import es from 'event-stream';
import uuid from 'uuid';
import {
  EventEmitter
}
from 'events';
import ldj from 'ldjson-stream';
import srs from 'node-srs';
import BBox from '../util/bbox';
import {
  Duplex
}
from 'stream';
import {
  types
}
from '../soql/mapper';
import logger from '../util/logger';
import conf from '../config';

var config = conf();
const scratchPrologue = "";
const scratchSeparator = "\n";
const scratchEpilogue = "";

const jsonPrologue = "[";
const jsonSeparator = ",";
const jsonEpilogue = "]";

const WGS84 = '+proj=longlat +ellps=WGS84 +no_defs';

class Layer extends Duplex {
  static get EMPTY() {
    return '__empty__';
  }

  constructor(columns, position, disk, spec) {
    if (position === undefined) throw new Error("Need a layer index!");
    super();
    this._position = position;

    this.columns = columns;
    this._count = 0;
    this._crsMap = {};
    this._projectTo = srs.parse(WGS84);
    this._spec = spec || {};

    this._bbox = new BBox();

    if (disk) {
      this._outName = '/tmp/import_' + uuid.v4() + '.ldjson';
      this._out = disk.allocate(this._outName);
      this._out.write(scratchPrologue);
    }
  }

  toJSON() {
    return {
      count: this.count,
      projection: this.defaultCrs.name,
      name: this.name,
      geometry: this.geomType,
      bbox: this._bbox.toJSON(),
      columns: this.columns.map((c) => c.toJSON())
    };
  }

  get scratchName() {
    return this._outName;
  }

  get name() {
    return this._spec.name || `layer_${this._position}`;
  }

  get uid() {
    return this._spec.uid || Layer.EMPTY;
  }

  set uid(uid) {
    this._spec.uid = uid;
    return this;
  }

  belongsIn(soqlRow) {
    for (var i = 0; i < this.columns.length; i++) {
      let column = this.columns[i];
      let soqlValue = soqlRow[i];

      let nameMatch = column.rawName === soqlValue.rawName;
      if (!nameMatch) return false;

      let typeMatch = column.ctype === soqlValue.ctype;
      let isEmpty = (column.ctype === 'null' || soqlValue.ctype === 'null');
      if (!typeMatch && !isEmpty) return false;
    }
    return true;
  }

  get geomType() {
    var geom = this.columns.find((col) => col.isGeometry);
    if (!geom) return null;
    return geom.dataTypeName;
  }

  set defaultCrs(urn) {
    this._defaultCrs = srs.parse(urn);
  }

  get defaultCrs() {
    return this._defaultCrs || this._projectTo;
  }

  get projections() {
    return Object.values(this._crsMap);
  }

  /**
   * Layers are created on the fly. They are created when we encounter the
   * a soqlRow which does not fit in the set of layers that we know about.
   * _updateColumnTypes mutates the Layer's column types as values that
   * have more type info are written to it.
   *
   * An Example:
   *   A dataset is being read which looks like
   *   [
   *     ['a', 1, POINT(1, 2), null],
   *     ['b', 1, POINT(2, 3), 'something']
   *     ['c', 1, POINT(3, 4), null]
   *   ]
   *
   *
   *   When we encounter the first row, a Layer, layer0, is created with the first soqlRow:
   *     ['a', 1, POINT(1, 2), null]
   *   So we end up with layer0 that has columns of type
   *     [SoQLText, SoQLNumber, SoQLPoint, SoQLNull]
   *
   *   Then we encounter the second row, row1, which has more info about what the last
   *   column actually is.
   *     ['b', 1, POINT(2, 3), 'something']
   *   layer0.belongsIn(row1) will return true, so row1 will get written to layer0.
   *   layer0.write will call _updateColumnTypes to update its undefined columns to reflect
   *   the fact that it is now storing values with more type info than it had before.
   *
   * This is a little confusing and gross, and comes from the fact that this is
   * being written to the lowest common denominator (geoJSON, KML, KMZ) of type
   * info. ESRI shapefiles give more hints about types, though all those hints
   * are eliminated in the shapefile library being used.
   *
   * This could be written as a big map, but it is called on every write,
   * so it's important to return as early as possible.
   *
   * @mutates this.columns
   */
  _updateColumnTypes(soqlRow) {
    var undefinedColumns = this.columns.filter((c) => c.ctype === 'null');
    if (!undefinedColumns.length) return;

    var definedSoqlCols = soqlRow.filter((c) => c.ctype !== 'null');
    //We want the set of columns that are defined for the value, but are not defined
    //for the layer. These are the columns that we will replace in the layer.
    var toAdd = definedSoqlCols.filter((valCol) => {
      return undefinedColumns.find((col) => valCol.rawName === col.rawName);
    });

    if (toAdd.length === 0) return;

    this.columns = this.columns.map((col) => {
      var valCol = toAdd.find((newCol) => newCol.rawName === col.rawName);
      var newCol;
      if (valCol) {
        newCol = new valCol.constructor(valCol.rawName);
        logger.debug(`Replacing old undefined column ${valCol.rawName} with new ${valCol.constructor.name} column`);
      }
      return newCol || col;
    });
  }

  /**
   * Write the soqlRow with a given crs to the layer.
   * This will only be called if belongsIn returned true for the soqlRow and this layer.
   * See the note above on the confusing mutate-y  bits.
   * @param  {[type]} crs       [description]
   * @param  {[type]} soqlRow   [description]
   * @param  {[type]} throwaway [description]
   * @return {[type]}           [description]
   */
  write(crs, soqlRow, throwaway, done) {
    if (crs) this._crsMap[this._count] = crs;

    this._updateColumnTypes(soqlRow);
    this._count++;
    if (throwaway) {
      if (done) done();
      return;
    }

    this._out.write(
      this._scratchSeparator(this._count) + JSON.stringify(soqlRow.map((r) => r.value)) + '\n',
      null, done);
  }

  _scratchSeparator(index) {
    return index === 0 ? '' : scratchSeparator;
  }

  _jsonSeparator(index) {
    return index === 0 ? '' : jsonSeparator;
  }

  close(cb) {
    this._out.write(scratchEpilogue, null, () => {
      this._out.on('finish', () => {
        this.emit('readable');
        this._emittedReadable = true;
        cb();
      });
      this._out.end();
    });
  }

  get count() {
    return this._count;
  }

  _expandBbox(geom) {
    //yes a map is silly here because this is a side effect on the layer
    //but it keeps things simple
    geom.mapCoordinates((coord) => {
      this._bbox.expand(coord);
    });
  }

  _getProjectionFor(index) {
    if (!this._crsMap[index]) return this.defaultCrs;
    return srs.parse(this._crsMap[index]);
  }

  _startPushing() {
    this._isPushing = true;
    var projectTo = this._projectTo;
    var index = 0;

    this.push(jsonPrologue);
    fs.createReadStream(this._outName)
      .pipe(ldj.parse())
      .pipe(es.map((row, cb) => {
        var soqlRow = _.zip(this.columns, row).map(([column, value]) => {
          var soql = new types[column.ctype](column.rawName, value);
          if (soql.isGeometry) {
            let projection = this._getProjectionFor(index);
            let geom = soql.reproject(projection, projectTo);
            this._expandBbox(geom);
            return geom;
          }
          return soql;
        });

        var sep = this._jsonSeparator(index);
        var ep = '';
        index++;
        if (index === this._count) ep = jsonEpilogue;

        var asSoda = soqlRow.reduce((acc, col) => {
          acc[col.name] = col.value;
          return acc;
        }, {});

        var rowString = sep + JSON.stringify(asSoda) + ep;
        this.push(rowString);
        cb();
      })).on('end', () => {
        this.push(null);
      });
  }

  /**
    Read the rows back out from the scratch file that was written
    as their SoQL reps, and reproject them into WGS84

    @sideeffect: update the layer's _bbox property after the geometry
                 is reprojected. this expands the bbox if the geometry
                 lies outside of the bbox. After all features have been
                 read then _bbox will encompass all of them
  */
  _read() {
    if (!this._emittedReadable && !this._isPushing) {
      this.once('readable', this._startPushing.bind(this));
    } else if (!this._isPushing) {
      this._startPushing();
    }
  }
}

export default Layer;<|endoftext|>


<commit_before>import h from 'snabbdom/h'
import {state} from '../state'
import {fullscreenIcon, playIcon, pauseIcon} from './icons'
import {FULL_SCREEN_CLICKED, FREEZER_CLICKED} from '../events'

const fullscreenComponent = ()=> h(
    'div',
    {
        on: {
            click: FULL_SCREEN_CLICKED,
        },
        style: {
            position: 'absolute',
            right: '5px',
            top: '0px',
            fontSize: '30px',
            height: '30px',
            cursor: 'pointer',
            padding: '10px',
            color: '#303030',
        },
    },
    [fullscreenIcon()]
)

const stopPlayComponent = ()=> h(
    'div',
    {
        on: {
            click: FREEZER_CLICKED,
        },
        style: {
            position: 'absolute',
            right: '55px',
            top: '0px',
            fontSize: '30px',
            height: '30px',
            cursor: 'pointer',
            padding: '10px',
            color: state.appIsFrozen ? 'rgb(91, 204, 91)' : 'rgb(204, 91, 91)',
        },
    },
    state.appIsFrozen ? [playIcon()] : [pauseIcon()]
)

export default ()=> h(
    'div',
    {
        style: {
            flex: '1 auto',
            height: '50px',
            maxHeight: '50px',
            minHeight: '50px',
            background: '#f8f8f8',
            boxShadow: 'rgba(0, 0, 0, 0.12) 0px 1px 6px, rgba(0, 0, 0, 0.12) 0px 1px 4px',
            display: 'flex',
            justifyContent: 'center',
            //paddingRight: state.editorRightWidth + 'px',
            //paddingLeft: state.editorLeftWidth + 'px',
        },
    },
    [
        h(
            'div',
            {
                style: {
                    fontSize: '20px',
                    fontWeight: '300',
                    color: '#8e8e8e',
                    position: 'absolute',
                    top: '17px',
                    left: '20px',
                    cursor: 'default',
                    userSelect: 'none',
                },
            },
            'Components'
        ),
        h(
            'a',
            {
                style: {
                    flex: '0 auto',
                    padding: '0 50px',
                    display: 'flex',
                    alignItems: 'center',
                    textDecoration: 'inherit',
                    userSelect: 'none',
                },
                attrs: { href: '/' },
            },
            [
                h('img', {
                    attrs: {
                        src: '/images/logo_new256x256.png',
                        height: '37',
                    },
                }),
            ]
        ),
        fullscreenComponent(),
        stopPlayComponent(),
    ]
)<commit_msg>small design fixes<commit_after>import h from 'snabbdom/h'
import {state} from '../state'
import {fullscreenIcon, playIcon, pauseIcon} from './icons'
import {FULL_SCREEN_CLICKED, FREEZER_CLICKED} from '../events'

const fullscreenComponent = ()=> h(
    'div',
    {
        on: {
            click: FULL_SCREEN_CLICKED,
        },
        style: {
            position: 'absolute',
            right: '5px',
            top: '0px',
            fontSize: '30px',
            height: '30px',
            cursor: 'pointer',
            padding: '10px',
            color: '#303030',
        },
    },
    [fullscreenIcon()]
)

const stopPlayComponent = ()=> h(
    'div',
    {
        on: {
            click: FREEZER_CLICKED,
        },
        style: {
            position: 'absolute',
            right: '55px',
            top: '0px',
            fontSize: '30px',
            height: '30px',
            cursor: 'pointer',
            padding: '10px',
            transition: '0.2s color',
            userSelect: 'none',
            color: state.appIsFrozen ? 'rgb(91, 204, 91)' : 'rgb(204, 91, 91)',
        },
    },
    state.appIsFrozen ? [playIcon()] : [pauseIcon()]
)

export default ()=> h(
    'div',
    {
        style: {
            flex: '1 auto',
            height: '50px',
            maxHeight: '50px',
            minHeight: '50px',
            background: '#f8f8f8',
            boxShadow: 'rgba(0, 0, 0, 0.12) 0px 1px 6px, rgba(0, 0, 0, 0.12) 0px 1px 4px',
            display: 'flex',
            justifyContent: 'center',
            //paddingRight: state.editorRightWidth + 'px',
            //paddingLeft: state.editorLeftWidth + 'px',
        },
    },
    [
        h(
            'div',
            {
                style: {
                    fontSize: '20px',
                    fontWeight: '300',
                    color: '#8e8e8e',
                    position: 'absolute',
                    top: '17px',
                    left: '20px',
                    cursor: 'default',
                    userSelect: 'none',
                },
            },
            'Components'
        ),
        h(
            'a',
            {
                style: {
                    flex: '0 auto',
                    padding: '0 50px',
                    display: 'flex',
                    alignItems: 'center',
                    textDecoration: 'inherit',
                    userSelect: 'none',
                },
                attrs: { href: '/' },
            },
            [
                h('img', {
                    attrs: {
                        src: '/images/logo_new256x256.png',
                        height: '37',
                    },
                }),
            ]
        ),
        fullscreenComponent(),
        stopPlayComponent(),
    ]
)<|endoftext|>


<commit_before>var test = require('tape')
var fs = require('fs')
var rm = require('rimraf')
var path = require('path')
var download = require('../download')
var util = require('../util')
var pkg = require('a-native-module/package')
var http = require('http')
var https = require('https')

var build = path.join(__dirname, 'build')
var unpacked = path.join(build, 'Release/leveldown.node')

test('downloading from GitHub, not cached', function (t) {
  t.plan(20)
  rm.sync(build)
  rm.sync(util.prebuildCache())

  var requestCount = 0
  var opts = {
    pkg: pkg,
    nolocal: true,
    rc: {platform: process.platform, arch: process.arch, path: __dirname},
    log: {
      http: function (type, message) {
        if (requestCount++ === 0) {
          t.equal(type, 'request', 'http request')
          t.equal(message, 'GET ' + downloadUrl)
        } else {
          t.equal(type, 200, 'status code logged')
          t.equal(message, downloadUrl)
        }
      },
      info: function (type, message) {
        t.equal(type, 'unpack', 'required module successfully')
        t.equal(message, 'required ' + unpacked + ' successfully')
      }
    }
  }

  var downloadUrl = util.getDownloadUrl(opts)
  var cachedPrebuild = util.cachedPrebuild(downloadUrl)
  var npmCache = util.npmCache()
  var tempFile

  var existsCallNum = 0
  var _access = fs.access ? fs.access.bind(fs) : fs.access
  var _exists = fs.exists.bind(fs)
  if (_access) {
    fs.access = function (path, a, cb) {
      if (existsCallNum++ === 0) {
        t.equal(path, npmCache, 'fs.exists called for npm cache')
        _access(path, cb)
      }
    }
  }
  fs.exists = function (path, cb) {
    if (existsCallNum++ === 0) {
      t.equal(path, npmCache, 'fs.exists called for npm cache')
      _exists(path, cb)
    } else {
      t.equal(path, cachedPrebuild, 'fs.exists called for prebuild')
      _exists(path, function (exists) {
        t.equal(exists, false, 'prebuild should be cached')
        cb(exists)
      })
    }
  }

  var mkdirCount = 0
  var _mkdir = fs.mkdir.bind(fs)
  fs.mkdir = function () {
    var args = [].slice.call(arguments)
    if (mkdirCount++ === 0) {
      t.equal(args[0], util.prebuildCache(), 'fs.mkdir called for prebuildCache')
    }
    _mkdir.apply(fs, arguments)
  }

  var writeStreamCount = 0
  var _createWriteStream = fs.createWriteStream.bind(fs)
  fs.createWriteStream = function (path) {
    if (writeStreamCount++ === 0) {
      tempFile = path
      t.ok(/\.tmp$/i.test(path), 'this is the temporary file')
    } else {
      t.ok(/\.node$/i.test(path), 'this is the unpacked file')
    }
    return _createWriteStream(path)
  }

  var _createReadStream = fs.createReadStream.bind(fs)
  fs.createReadStream = function (path) {
    t.equal(path, cachedPrebuild, 'createReadStream called for cachedPrebuild')
    return _createReadStream(path)
  }

  var _request = https.request
  https.request = function (opts) {
    https.request = _request
    t.equal('https://' + opts.hostname + opts.path, downloadUrl, 'correct url')
    return _request.apply(https, arguments)
  }

  t.equal(fs.existsSync(build), false, 'no build folder')

  download(opts, function (err) {
    t.error(err, 'no error')
    t.equal(fs.existsSync(util.prebuildCache()), true, 'prebuildCache created')
    t.equal(fs.existsSync(cachedPrebuild), true, 'prebuild was cached')
    t.equal(fs.existsSync(unpacked), true, unpacked + ' should exist')
    t.equal(fs.existsSync(tempFile), false, 'temp file should be gone')
    fs.exists = _exists
    fs.access = _access
    fs.mkdir = _mkdir
    fs.createWriteStream = _createWriteStream
    fs.createReadStream = _createReadStream
  })
})

test('cached prebuild', function (t) {
  t.plan(10)
  rm.sync(build)

  var opts = {
    pkg: pkg,
    nolocal: true,
    rc: {platform: process.platform, arch: process.arch, path: __dirname},
    log: {
      info: function (type, message) {
        t.equal(type, 'unpack', 'required module successfully')
        t.equal(message, 'required ' + unpacked + ' successfully')
      }
    }
  }

  var downloadUrl = util.getDownloadUrl(opts)
  var cachedPrebuild = util.cachedPrebuild(downloadUrl)
  var npmCache = util.npmCache()

  var existsCallNum = 0
  var _access = fs.access ? fs.access.bind(fs) : fs.access
  var _exists = fs.exists.bind(fs)
  if (_access) {
    fs.access = function (path, a, cb) {
      if (existsCallNum++ === 0) {
        t.equal(path, npmCache, 'fs.exists called for npm cache')
        _access(path, cb)
      }
    }
  }
  fs.exists = function (path, cb) {
    if (existsCallNum++ === 0) {
      t.equal(path, npmCache, 'fs.exists called for npm cache')
      _exists(path, cb)
    } else {
      t.equal(path, cachedPrebuild, 'fs.exists called for prebuild')
      _exists(path, function (exists) {
        t.equal(exists, true, 'prebuild should be cached')
        cb(exists)
      })
    }
  }

  var _createWriteStream = fs.createWriteStream.bind(fs)
  fs.createWriteStream = function (path) {
    t.ok(/\.node$/i.test(path), 'this is the unpacked file')
    return _createWriteStream(path)
  }

  var _createReadStream = fs.createReadStream.bind(fs)
  fs.createReadStream = function (path) {
    t.equal(path, cachedPrebuild, 'createReadStream called for cachedPrebuild')
    return _createReadStream(path)
  }

  t.equal(fs.existsSync(build), false, 'no build folder')

  download(opts, function (err) {
    t.error(err, 'no error')
    t.equal(fs.existsSync(unpacked), true, unpacked + ' should exist')
    fs.createReadStream = _createReadStream
    fs.createWriteStream = _createWriteStream
    fs.exists = _exists
    fs.access = _access
  })
})

test('missing .node file in .tar.gz should fail', function (t) {
  t.plan(2)

  var opts = {
    pkg: pkg,
    nolocal: true,
    rc: {platform: process.platform, arch: process.arch, path: __dirname},
    updateName: function (entry) {
      t.ok(/\.node$/i.test(entry.name), 'should match but we pretend it does not')
    }
  }

  download(opts, function (err) {
    t.equal(err.message, 'Missing .node file in archive', 'correct error message')
    t.end()
  })
})

test('non existing host should fail with no dangling temp file', function (t) {
  t.plan(5)

  var opts = {
    pkg: pkg,
    nolocal: true,
    rc: {platform: process.platform, arch: process.arch},
    log: {
      http: function (type, message) {
        t.equal(type, 'request', 'http request')
        t.equal(message, 'GET ' + downloadUrl)
      }
    }
  }
  opts.pkg.binary = {
    host: 'https://foo.bar.baz'
  }

  var downloadUrl = util.getDownloadUrl(opts)
  var cachedPrebuild = util.cachedPrebuild(downloadUrl)

  var _createWriteStream = fs.createWriteStream.bind(fs)
  fs.createWriteStream = function (path) {
    t.ok(false, 'no temporary file should be written')
    return _createWriteStream(path)
  }

  t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached')

  download(opts, function (err) {
    t.ok(err, 'should error')
    t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached')
    fs.createWriteStream = _createWriteStream
  })
})

test('existing host but invalid url should fail', function (t) {
  t.plan(7)

  var requestCount = 0
  var opts = {
    pkg: pkg,
    nolocal: true,
    rc: {platform: process.platform, arch: process.arch},
    log: {
      http: function (type, message) {
        if (requestCount >= 2) return
        if (requestCount++ === 0) {
          t.equal(type, 'request', 'http request')
          t.equal(message, 'GET ' + downloadUrl)
        } else {
          t.equal(type, 404, 'invalid resource')
          t.equal(message, downloadUrl)
        }
      }
    }
  }
  opts.pkg.binary = {
    host: 'http://localhost:8888',
    remote_path: 'prebuilds',
    package_name: 'woohooo-{abi}'
  }

  var downloadUrl = util.getDownloadUrl(opts)
  var cachedPrebuild = util.cachedPrebuild(downloadUrl)

  var server = http.createServer(function (req, res) {
    t.equal(req.url, '/prebuilds/woohooo-' + process.versions.modules, 'correct url')
    res.statusCode = 404
    res.end()
  }).listen(8888, function () {
    download(opts, function (err) {
      t.equal(err.message, 'Prebuilt binaries for node version ' + process.version + ' are not available', 'correct error')
      t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached')
      t.end()
      server.unref()
    })
  })
})

test('error during download should fail with no dangling temp file', function (t) {
  t.plan(7)

  var downloadError = new Error('something went wrong during download')
  var opts = {
    pkg: pkg,
    nolocal: true,
    rc: {platform: process.platform, arch: process.arch},
    log: {http: function () { }}
  }
  opts.pkg.binary = {
    host: 'http://localhost:8889',
    remote_path: 'prebuilds',
    package_name: 'woohooo-{abi}'
  }

  var downloadUrl = util.getDownloadUrl(opts)
  var cachedPrebuild = util.cachedPrebuild(downloadUrl)
  var tempFile

  var _createWriteStream = fs.createWriteStream.bind(fs)
  fs.createWriteStream = function (path) {
    tempFile = path
    t.ok(/\.tmp$/i.test(path), 'this is the temporary file')
    return _createWriteStream(path)
  }

  var _request = http.request
  http.request = function (opts) {
    http.request = _request
    t.equal('http://' + opts.hostname + ':' + opts.port + opts.path, downloadUrl, 'correct url')
    var wrapped = arguments[1]
    arguments[1] = function (res) {
      t.equal(res.statusCode, 200, 'correct statusCode')
      // simulates error during download
      setTimeout(function () { res.emit('error', downloadError) }, 10)
      wrapped(res)
    }
    return _request.apply(http, arguments)
  }

  var server = http.createServer(function (req, res) {
    t.equal(req.url, '/prebuilds/woohooo-' + process.versions.modules, 'correct url')
    res.statusCode = 200
    res.write('yep') // simulates hanging request
  }).listen(8889, function () {
    download(opts, function (err) {
      t.equal(err.message, downloadError.message, 'correct error')
      t.equal(fs.existsSync(tempFile), false, 'no dangling temp file')
      t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached')
      t.end()
      fs.createWriteStream = _createWriteStream
      server.unref()
    })
  })
})
<commit_msg>skip testing log outputs + refactor options code<commit_after>var test = require('tape')
var fs = require('fs')
var rm = require('rimraf')
var path = require('path')
var download = require('../download')
var util = require('../util')
var http = require('http')
var https = require('https')

var build = path.join(__dirname, 'build')
var unpacked = path.join(build, 'Release/leveldown.node')

test('downloading from GitHub, not cached', function (t) {
  t.plan(14)
  rm.sync(build)
  rm.sync(util.prebuildCache())

  var opts = getOpts()
  var downloadUrl = util.getDownloadUrl(opts)
  var cachedPrebuild = util.cachedPrebuild(downloadUrl)
  var npmCache = util.npmCache()
  var tempFile

  var existsCallNum = 0
  var _access = fs.access ? fs.access.bind(fs) : fs.access
  var _exists = fs.exists.bind(fs)
  if (_access) {
    fs.access = function (path, a, cb) {
      if (existsCallNum++ === 0) {
        t.equal(path, npmCache, 'fs.exists called for npm cache')
        _access(path, cb)
      }
    }
  }
  fs.exists = function (path, cb) {
    if (existsCallNum++ === 0) {
      t.equal(path, npmCache, 'fs.exists called for npm cache')
      _exists(path, cb)
    } else {
      t.equal(path, cachedPrebuild, 'fs.exists called for prebuild')
      _exists(path, function (exists) {
        t.equal(exists, false, 'prebuild should be cached')
        cb(exists)
      })
    }
  }

  var mkdirCount = 0
  var _mkdir = fs.mkdir.bind(fs)
  fs.mkdir = function () {
    var args = [].slice.call(arguments)
    if (mkdirCount++ === 0) {
      t.equal(args[0], util.prebuildCache(), 'fs.mkdir called for prebuildCache')
    }
    _mkdir.apply(fs, arguments)
  }

  var writeStreamCount = 0
  var _createWriteStream = fs.createWriteStream.bind(fs)
  fs.createWriteStream = function (path) {
    if (writeStreamCount++ === 0) {
      tempFile = path
      t.ok(/\.tmp$/i.test(path), 'this is the temporary file')
    } else {
      t.ok(/\.node$/i.test(path), 'this is the unpacked file')
    }
    return _createWriteStream(path)
  }

  var _createReadStream = fs.createReadStream.bind(fs)
  fs.createReadStream = function (path) {
    t.equal(path, cachedPrebuild, 'createReadStream called for cachedPrebuild')
    return _createReadStream(path)
  }

  var _request = https.request
  https.request = function (opts) {
    https.request = _request
    t.equal('https://' + opts.hostname + opts.path, downloadUrl, 'correct url')
    return _request.apply(https, arguments)
  }

  t.equal(fs.existsSync(build), false, 'no build folder')

  download(opts, function (err) {
    t.error(err, 'no error')
    t.equal(fs.existsSync(util.prebuildCache()), true, 'prebuildCache created')
    t.equal(fs.existsSync(cachedPrebuild), true, 'prebuild was cached')
    t.equal(fs.existsSync(unpacked), true, unpacked + ' should exist')
    t.equal(fs.existsSync(tempFile), false, 'temp file should be gone')
    fs.exists = _exists
    fs.access = _access
    fs.mkdir = _mkdir
    fs.createWriteStream = _createWriteStream
    fs.createReadStream = _createReadStream
  })
})

test('cached prebuild', function (t) {
  t.plan(8)
  rm.sync(build)

  var opts = getOpts()
  var downloadUrl = util.getDownloadUrl(opts)
  var cachedPrebuild = util.cachedPrebuild(downloadUrl)
  var npmCache = util.npmCache()

  var existsCallNum = 0
  var _access = fs.access ? fs.access.bind(fs) : fs.access
  var _exists = fs.exists.bind(fs)
  if (_access) {
    fs.access = function (path, a, cb) {
      if (existsCallNum++ === 0) {
        t.equal(path, npmCache, 'fs.exists called for npm cache')
        _access(path, cb)
      }
    }
  }
  fs.exists = function (path, cb) {
    if (existsCallNum++ === 0) {
      t.equal(path, npmCache, 'fs.exists called for npm cache')
      _exists(path, cb)
    } else {
      t.equal(path, cachedPrebuild, 'fs.exists called for prebuild')
      _exists(path, function (exists) {
        t.equal(exists, true, 'prebuild should be cached')
        cb(exists)
      })
    }
  }

  var _createWriteStream = fs.createWriteStream.bind(fs)
  fs.createWriteStream = function (path) {
    t.ok(/\.node$/i.test(path), 'this is the unpacked file')
    return _createWriteStream(path)
  }

  var _createReadStream = fs.createReadStream.bind(fs)
  fs.createReadStream = function (path) {
    t.equal(path, cachedPrebuild, 'createReadStream called for cachedPrebuild')
    return _createReadStream(path)
  }

  t.equal(fs.existsSync(build), false, 'no build folder')

  download(opts, function (err) {
    t.error(err, 'no error')
    t.equal(fs.existsSync(unpacked), true, unpacked + ' should exist')
    fs.createReadStream = _createReadStream
    fs.createWriteStream = _createWriteStream
    fs.exists = _exists
    fs.access = _access
  })
})

test('missing .node file in .tar.gz should fail', function (t) {
  t.plan(2)

  var opts = getOpts()
  opts.updateName = function (entry) {
    t.ok(/\.node$/i.test(entry.name), 'should match but we pretend it does not')
  }
  download(opts, function (err) {
    t.equal(err.message, 'Missing .node file in archive', 'correct error message')
    t.end()
  })
})

test('non existing host should fail with no dangling temp file', function (t) {
  t.plan(3)

  var opts = getOpts()
  opts.pkg.binary = {
    host: 'https://foo.bar.baz'
  }

  var downloadUrl = util.getDownloadUrl(opts)
  var cachedPrebuild = util.cachedPrebuild(downloadUrl)

  var _createWriteStream = fs.createWriteStream.bind(fs)
  fs.createWriteStream = function (path) {
    t.ok(false, 'no temporary file should be written')
    return _createWriteStream(path)
  }

  t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached')

  download(opts, function (err) {
    t.ok(err, 'should error')
    t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached')
    fs.createWriteStream = _createWriteStream
  })
})

test('existing host but invalid url should fail', function (t) {
  t.plan(3)

  var opts = getOpts()
  opts.pkg.binary = {
    host: 'http://localhost:8888',
    remote_path: 'prebuilds',
    package_name: 'woohooo-{abi}'
  }

  var downloadUrl = util.getDownloadUrl(opts)
  var cachedPrebuild = util.cachedPrebuild(downloadUrl)

  var server = http.createServer(function (req, res) {
    t.equal(req.url, '/prebuilds/woohooo-' + process.versions.modules, 'correct url')
    res.statusCode = 404
    res.end()
  }).listen(8888, function () {
    download(opts, function (err) {
      t.equal(err.message, 'Prebuilt binaries for node version ' + process.version + ' are not available', 'correct error')
      t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached')
      t.end()
      server.unref()
    })
  })
})

test('error during download should fail with no dangling temp file', function (t) {
  t.plan(7)

  var downloadError = new Error('something went wrong during download')

  var opts = getOpts()
  opts.pkg.binary = {
    host: 'http://localhost:8889',
    remote_path: 'prebuilds',
    package_name: 'woohooo-{abi}'
  }

  var downloadUrl = util.getDownloadUrl(opts)
  var cachedPrebuild = util.cachedPrebuild(downloadUrl)
  var tempFile

  var _createWriteStream = fs.createWriteStream.bind(fs)
  fs.createWriteStream = function (path) {
    tempFile = path
    t.ok(/\.tmp$/i.test(path), 'this is the temporary file')
    return _createWriteStream(path)
  }

  var _request = http.request
  http.request = function (opts) {
    http.request = _request
    t.equal('http://' + opts.hostname + ':' + opts.port + opts.path, downloadUrl, 'correct url')
    var wrapped = arguments[1]
    arguments[1] = function (res) {
      t.equal(res.statusCode, 200, 'correct statusCode')
      // simulates error during download
      setTimeout(function () { res.emit('error', downloadError) }, 10)
      wrapped(res)
    }
    return _request.apply(http, arguments)
  }

  var server = http.createServer(function (req, res) {
    t.equal(req.url, '/prebuilds/woohooo-' + process.versions.modules, 'correct url')
    res.statusCode = 200
    res.write('yep') // simulates hanging request
  }).listen(8889, function () {
    download(opts, function (err) {
      t.equal(err.message, downloadError.message, 'correct error')
      t.equal(fs.existsSync(tempFile), false, 'no dangling temp file')
      t.equal(fs.existsSync(cachedPrebuild), false, 'nothing cached')
      t.end()
      fs.createWriteStream = _createWriteStream
      server.unref()
    })
  })
})

function getOpts () {
  return {
    pkg: require('a-native-module/package'),
    nolocal: true,
    rc: {platform: process.platform, arch: process.arch, path: __dirname},
    log: {http: function (type, message) {}, info: function (type, message) {}}
  }
}
<|endoftext|>


<commit_before>/* global webuiUrl, ipfs, page, describe, it, beforeAll, expect */

describe('IPNS publishing', () => {
  beforeAll(async () => {
    await page.goto(webuiUrl + '#/settings', { waitUntil: 'networkidle' })
  })

  const keyName = 'pet-name-e2e-ipns-test'

  describe('Settings screen', () => {
    it('should list IPNS keys', async () => {
      await page.goto(webuiUrl + '#/settings', { waitUntil: 'networkidle' })
      // confirm the self key is displayed
      await waitForIPNSKeyList(ipfs, 'self')
    })

    it('should have a clickable "Generate key" button', async () => {
      const genKey = 'text=Generate Key'
      await page.waitForSelector(genKey)
      await page.click(genKey)
      await page.waitForSelector('div[role="dialog"]')
      await page.waitForSelector('text=Enter pet name of key to create')
    })

    it('should list new IPNS key with provided pet name ', async () => {
      // provide key name
      await page.type('div[role="dialog"] input[type="text"]', keyName)
      // hit Enter
      await page.keyboard.type('\n')
      // expect it to be added to key list under provided pet name
      await waitForIPNSKeyList(ipfs, 'self')
    })
  })

  describe('Files screen', () => {
    beforeAll(async () => {
      await page.goto(webuiUrl + '#/files', { waitUntil: 'networkidle' })
    })

    const testFilename = 'ipns-test.txt'
    const testCid = '/ipfs/bafyaaeqkcaeaeeqknfyg44znorsxg5akdafa'

    it('should have "Publish to IPNS" context action', async () => {
      // first: create a test file
      const button = 'button[id="import-button"]'
      await page.waitForSelector(button, { state: 'visible' })
      await page.click(button)
      await page.waitForSelector('#add-by-path', { state: 'visible' })
      page.click('button[id="add-by-path"]')
      await page.waitForSelector('div[role="dialog"] input[name="name"]')
      await page.fill('div[role="dialog"] input[name="path"]', testCid)
      await page.fill('div[role="dialog"] input[name="name"]', testFilename)
      await page.keyboard.type('\n')
      // expect file with matching filename to be added to the file list
      await page.waitForSelector(`.File:has-text("${testFilename}")`)
      // click on the context menu
      await page.click(`.File:has-text('${testFilename}') .file-context-menu`)
      // click on the IPNS action
      await page.waitForSelector(`.File:has-text("${testFilename}")`)
      // expect IPNS action to be present in the context menu
      await page.waitForSelector('button:has-text("Publish to IPNS")')
    })

    it('should allow selecting IPNS keys', async () => {
      // .. continue by clicking on context action
      await page.click('button:has-text("Publish to IPNS")')
      await page.waitForSelector('div[role="dialog"] .publishModalKeys')
      await page.click(`div[role="dialog"] .publishModalKeys button:has-text("${keyName}")`)
      await page.click(`text=${keyName}`)
      const publishButton = 'div[role="dialog"] button:has-text("Publish")'
      const enabled = await page.isEnabled(publishButton)
      expect(enabled).toBeTruthy()
      await page.click(publishButton)
    })

    /*
    it('should execute IPNS publish and reflect that on Settings screen', async () => {
      await page.click('a:has-text("Settings")')
      await waitForIPNSKeyList(ipfs, keyName)
      // inspect link behind key id -- it should point at CID we used for publishing
      const { id } = await ipns.key.list().filter(k => k.name === keyName)[0]
      const keyIdCell = `a:has-text("${id}")`
      await page.waitForSelector(keyIdCell)
      const href = await page.getAttribute(keyIdCell, 'href')
      expect(href.includes(testCid)).toBeTruthy()
    })
    */
  })
})

// Confirm contents of IPNS Publishing Keys table on Settings screen
// are in sync with ipfs.key.list
async function waitForIPNSKeyList (ipfs, specificKey) {
  await page.waitForSelector('text=IPNS Publishing Keys')
  if (specificKey) await page.waitForSelector(`text=${specificKey}`)
  for (const { id, name } of await ipfs.key.list()) {
    await page.waitForSelector(`text=${id}`)
    await page.waitForSelector(`text=${name}`)
  }
}
<commit_msg>test(ipns): confirm IPNS record was published<commit_after>/* global webuiUrl, ipfs, page, describe, it, beforeAll, afterAll, expect */

const { createController } = require('ipfsd-ctl')

describe('IPNS publishing', () => {
  const keyName = 'pet-name-e2e-ipns-test'
  let ipfsd
  let peeraddr

  beforeAll(async () => {
    // spawn a second ephemeral local node as a peer for ipns publishing
    ipfsd = await createController({
      type: 'go',
      ipfsBin: require('go-ipfs').path(),
      ipfsHttpModule: require('ipfs-http-client'),
      test: true,
      disposable: true
    })
    const { addresses } = await ipfsd.api.id()
    peeraddr = addresses.find((ma) => ma.toString().startsWith('/ip4/127.0.0.1')).toString()
  })

  describe('Settings screen', () => {
    beforeAll(async () => {
      await page.goto(webuiUrl + '#/settings', { waitUntil: 'networkidle' })
    })
    it('should list IPNS keys', async () => {
      await page.goto(webuiUrl + '#/settings', { waitUntil: 'networkidle' })
      // confirm the self key is displayed
      await waitForIPNSKeyList(ipfs, 'self')
    })

    it('should have a clickable "Generate key" button', async () => {
      const genKey = 'text=Generate Key'
      await page.waitForSelector(genKey)
      await page.click(genKey)
      await page.waitForSelector('div[role="dialog"]')
      await page.waitForSelector('text=Enter pet name of key to create')
    })

    it('should list new IPNS key with provided pet name ', async () => {
      // provide key name
      await page.type('div[role="dialog"] input[type="text"]', keyName)
      // hit Enter
      await page.keyboard.type('\n')
      // expect it to be added to key list under provided pet name
      await waitForIPNSKeyList(ipfs, 'self')
    })
  })

  describe('Files screen', () => {
    beforeAll(async () => {
      await page.goto(webuiUrl + '#/files', { waitUntil: 'networkidle' })
    })

    const testFilename = 'ipns-test.txt'
    const testCid = '/ipfs/bafyaaeqkcaeaeeqknfyg44znorsxg5akdafa'

    it('should have "Publish to IPNS" context action', async () => {
      // first: create a test file
      const button = 'button[id="import-button"]'
      await page.waitForSelector(button, { state: 'visible' })
      await page.click(button)
      await page.waitForSelector('#add-by-path', { state: 'visible' })
      page.click('button[id="add-by-path"]')
      await page.waitForSelector('div[role="dialog"] input[name="name"]')
      await page.fill('div[role="dialog"] input[name="path"]', testCid)
      await page.fill('div[role="dialog"] input[name="name"]', testFilename)
      await page.keyboard.type('\n')
      // expect file with matching filename to be added to the file list
      await page.waitForSelector(`.File:has-text("${testFilename}")`)
      // click on the context menu
      await page.click(`.File:has-text('${testFilename}') .file-context-menu`)
      // click on the IPNS action
      await page.waitForSelector(`.File:has-text("${testFilename}")`)
      // expect IPNS action to be present in the context menu
      await page.waitForSelector('button:has-text("Publish to IPNS")')
    })

    it('should allow selecting IPNS keys', async () => {
      // .. continue by clicking on context action
      await page.click('button:has-text("Publish to IPNS")')
      await page.waitForSelector('div[role="dialog"] .publishModalKeys')
      await page.click(`div[role="dialog"] .publishModalKeys button:has-text("${keyName}")`)
      await page.click(`text=${keyName}`)
      const publishButton = 'div[role="dialog"] button:has-text("Publish")'
      const enabled = await page.isEnabled(publishButton)
      expect(enabled).toBeTruthy()
      // connect to other peer to have something in the peer table
      // (ipns will fail to publish without peers)
      await ipfs.swarm.connect(peeraddr)
      await page.click(publishButton)
      await page.waitForSelector('text=Successfully published')
      await page.click('button:has-text("Copy")')
      // confirm IPNS record in local store points at the CID
      const { id } = (await ipfs.key.list()).filter(k => k.name === keyName)[0]
      for await (const name of ipfs.name.resolve(`/ipns/${id}`, { recursive: true })) {
        expect(name).toEqual(testCid)
      }
    })
  })

  afterAll(async () => {
    if (ipfsd) await ipfsd.stop()
  })
})

// Confirm contents of IPNS Publishing Keys table on Settings screen
// are in sync with ipfs.key.list
async function waitForIPNSKeyList (ipfs, specificKey) {
  await page.waitForSelector('text=IPNS Publishing Keys')
  if (specificKey) await page.waitForSelector(`text=${specificKey}`)
  for (const { id, name } of await ipfs.key.list()) {
    await page.waitForSelector(`text=${id}`)
    await page.waitForSelector(`text=${name}`)
  }
}
<|endoftext|>


<commit_before>"use strict";

var errors = require('../server/errors.js')
  , request = require('request')
  , _ = require('underscore')
  , exec = require('child_process').exec
  , status = require("../server/status.js")
  , logger = require('../server/logger.js').get('appium')
  , logDeprecationWarning = require('../helpers.js').logDeprecationWarning;

var UnknownError = errors.UnknownError
  , ProtocolError = errors.ProtocolError;

exports.respond = function (response, cb) {
  if (typeof response === 'undefined') {
    cb(null, '');
  } else {
    if (typeof(response) !== "object") {
      cb(new UnknownError(), response);
    } else if (!('status' in response)) {
      cb(new ProtocolError('Status missing in response from device'), response);
    } else {
      var status = parseInt(response.status, 10);
      if (isNaN(status)) {
        cb(new ProtocolError('Invalid status in response from device'), response);
      } else {
        response.status = status;
        cb(null, response);
      }
    }
  }
};

exports.proxy = function (command, cb) {
  logger.debug('Pushing command to appium work queue: ' + JSON.stringify(command));
  this.push([command, cb]);
};

exports.proxyWithMinTime = function (command, ms, cb) {
  var start = Date.now();
  logger.debug('Pushing command to appium work queue: ' + JSON.stringify(command));
  this.push([command, function () {
    var args = Array.prototype.slice.call(arguments, 0);
    var waitNeeded = ms - (Date.now() - start);
    if (waitNeeded > 0) {
      setTimeout(function () {
        cb.apply(null, args);
      }, waitNeeded);
    } else {
      cb.apply(null, args);
    }
  }]);
};

exports.resetTimeout = function () {
  if (this.onResetTimeout) this.onResetTimeout();
};

exports.waitForCondition = function (waitMs, condFn, cb, intervalMs) {
  if (typeof intervalMs === "undefined") {
    intervalMs = 500;
  }
  var begunAt = Date.now();
  var endAt = begunAt + waitMs;
  var spin = function () {
    condFn(function (condMet) {
      var now = Date.now();
      var waited = now - begunAt;
      var args = Array.prototype.slice.call(arguments);
      if (condMet) {
        cb.apply(this, args.slice(1));
      } else if (now < endAt) {
        logger.debug("Waited for " + waited + "ms so far");
        setTimeout(spin, intervalMs);
      } else {
        logger.debug("Condition unmet after " + waited + "ms. Timing out.");
        cb.apply(this, args.slice(1));
      }
    }.bind(this));
  }.bind(this);
  spin();
};

exports.implicitWaitForCondition = function (condFn, cb) {
  logger.debug("Waiting up to " + this.implicitWaitMs + "ms for condition");
  var _condFn = condFn;
  condFn = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    this.resetTimeout();
    _condFn.apply(this, args);
  }.bind(this);
  this.waitForCondition(this.implicitWaitMs, condFn, cb);
};

exports.doRequest = function (url, method, body, contentType, cb) {
  if (typeof cb === "undefined" && typeof contentType === "function") {
    cb = contentType;
    contentType = null;
  }
  if (typeof contentType === "undefined" || contentType === null) {
    contentType = "application/json;charset=UTF-8";
  }
  if (!(/^https?:\/\//.exec(url))) {
    url = 'http://' + url;
  }
  var opts = {
    url: url
  , method: method
  };
  if (_.contains(['put', 'post', 'patch'], method.toLowerCase())) {
    if (typeof body === "object") {
      opts.json = body;
    } else {
      opts.body = body || "";
    }
  }
  // explicitly set these headers with correct capitalization to work around
  // an issue in node/requests
  logger.debug("Making http request with opts: " + JSON.stringify(opts));
  request(opts, function (err, res, body) {
    cb(err, res, body);
  });
};

exports.isAppInstalled = function (isInstalledCommand, cb) {
  logger.debug("Checking app install status using: " + isInstalledCommand);
  exec(isInstalledCommand, function (error, stdout) {
    cb(error, stdout);
  });
};

exports.removeApp = function (removeCommand, udid, bundleId, cb) {
  logger.debug("Removing app using cmd: " + removeCommand);
  exec(removeCommand, function (error) {
    if (error !== null) {
      cb(new Error('Unable to un-install [' + bundleId + '] from device with id [' + udid + ']. Error [' + error + ']'));
    } else {
      cb(error, 'Successfully un-installed [' + bundleId + '] from device with id [' + udid + ']');
    }
  });
};

exports.installApp = function (installationCommand, udid, unzippedAppPath, cb) {
  logger.debug("Installing app using cmd: " + installationCommand);
  exec(installationCommand, { maxBuffer: 524288 }, function (error) {
    if (error !== null) {
      cb(new Error('Unable to install [' + unzippedAppPath + '] to device with id [' + udid + ']. Error [' + error + ']'));
    } else {
      cb(error, 'Successfully unzipped and installed [' + unzippedAppPath + '] to device with id [' + udid + ']');
    }
  });
};

exports.unpackApp = function (req, packageExtension, cb) {
  var reqAppPath = req.body.appPath;
  if (reqAppPath.toLowerCase().substring(0, 4) === "http") {
    req.device.downloadAndUnzipApp(reqAppPath, function (err, appPath) {
      cb(appPath);
    });
  } else if (reqAppPath.toLowerCase().substring(reqAppPath.length - 4) === ".zip") {
    req.device.unzipLocalApp(reqAppPath, function (err, appPath) {
      cb(appPath);
    });
  } else if (reqAppPath.toLowerCase().substring(reqAppPath.length - 4) === packageExtension) {
    cb(reqAppPath.toString());
  } else {
    cb(null);
  }
};

exports.proxyTo = function (endpoint, method, data, cb) {
  if (endpoint[0] !== '/') {
    endpoint = '/' + endpoint;
  }
  var url = 'http://' + this.proxyHost + ':' + this.proxyPort + endpoint;
  exports.doRequest(url, method, data ? data : '', cb);
};

exports.parseExecuteResponse = function (response, cb) {
  if ((response.value !== null) && (typeof response.value !== "undefined")) {
    var wdElement = null;
    if (!_.isArray(response.value)) {
      if (typeof response.value.ELEMENT !== "undefined") {
        wdElement = response.value.ELEMENT;
        wdElement = this.parseElementResponse(response.value);
        if (wdElement === null) {
          cb(null, {
            status: status.codes.UnknownError.code
          , value: "Error converting element ID atom for using in WD: " + response.value.ELEMENT
          });
        }
        response.value = wdElement;
      }
    } else {
      var args = response.value;
      for (var i = 0; i < args.length; i++) {
        wdElement = args[i];
        if ((args[i] !== null) && (typeof args[i].ELEMENT !== "undefined")) {
          wdElement = this.parseElementResponse(args[i]);
          if (wdElement === null) {
            cb(null, {
              status: status.codes.UnknownError.code
            , value: "Error converting element ID atom for using in WD: " + args[i].ELEMENT
            });
            return;
          }
          args[i] = wdElement;
        }
      }
      response.value = args;
    }
  }
  return response;
};

exports.checkValidLocStrat = function (strat, includeWeb, cb) {
  if (typeof includeWeb === "undefined") {
    includeWeb = false;
  }
  var validStrats = [
    'xpath',
    'id',
    'name',
    'class name'
  ];
  var nativeStrats = [
    '-ios uiautomation',
    'accessibility id',
    '-android uiautomator'
  ];
  var webStrats = [
    'link text',
    'css selector',
    'tag name',
    'partial link text'
  ];
  var nativeDeprecations = {};
  var webDeprecations = {};
  var deprecations = {name: 'accessibility id'};

  if (includeWeb) {
    validStrats = validStrats.concat(webStrats);
    deprecations = _.extend(deprecations, webDeprecations);
  } else {
    validStrats = validStrats.concat(nativeStrats);
    deprecations = _.extend(deprecations, nativeDeprecations);
  }
  if (!_.contains(validStrats, strat)) {
    logger.debug("Invalid locator strategy: " + strat);
    cb(null, {
      status: status.codes.UnknownCommand.code,
      value: {message: "Invalid locator strategy: " + strat}
    });
    return false;
  } else {
    if (_.has(deprecations, strat)) {
      logDeprecationWarning('locator strategy', strat, deprecations[strat]);
    }
    return true;
  }
};

exports.parseElementResponse = function (element) {
  var objId = element.ELEMENT
    , clientId = (5000 + this.webElementIds.length).toString();
  this.webElementIds.push(objId);
  return {ELEMENT: clientId};
};

exports.getAtomsElement = function (wdId) {
  var atomsId;
  try {
    atomsId = this.webElementIds[parseInt(wdId, 10) - 5000];
  } catch (e) {
    return null;
  }
  if (typeof atomsId === "undefined") {
    return null;
  }
  return {'ELEMENT': atomsId};
};

exports.useAtomsElement = function (elementId, failCb, cb) {
  if (parseInt(elementId, 10) < 5000) {
    logger.debug("Element with id " + elementId + " passed in for use with " +
                "atoms, but it's out of our internal scope. Adding 5000");
    elementId = (parseInt(elementId, 10) + 5000).toString();
  }
  var atomsElement = this.getAtomsElement(elementId);
  if (atomsElement === null) {
    failCb(null, {
      status: status.codes.UnknownError.code
    , value: "Error converting element ID for using in WD atoms: " + elementId
    });
  } else {
    cb(atomsElement);
  }
};

exports.convertElementForAtoms = function (args, cb) {
  for (var i = 0; i < args.length; i++) {
    if (args[i] !== null && typeof args[i].ELEMENT !== "undefined") {
      var atomsElement = this.getAtomsElement(args[i].ELEMENT);
      if (atomsElement === null) {
        cb(true, {
          status: status.codes.UnknownError.code
        , value: "Error converting element ID for using in WD atoms: " + args[i].ELEMENT
        });
        return;
      }
      args[i] = atomsElement;
    }
  }
  cb(null, args);
};

exports.jwpError = function (err, code, cb) {
  if (typeof code === "function") {
    cb = code;
    code = null;
  }
  if (code) {
    return cb(null, {
      status: code
    , value: err.message
    });
  }
  return cb(err);
};

exports.jwpSuccess = function (val, cb) {
  if (typeof val === "function") {
    cb = val;
    val = null;
  }
  return cb(null, {
    status: status.codes.Success.code
  , value: val
  });
};

exports.jwpResponse = function (err, val, cb) {
  if (typeof err === "function") {
    return exports.jwpSuccess(err);
  }
  if (typeof val === "function") {
    cb = val;
    val = null;
  }
  if (err) {
    return exports.jwpError(err, status.codes.UnknownError.code, cb);
  }
  return exports.jwpSuccess(val, cb);
};

// methods for appium session Settings
// These should really be on a Session object instead of the Device, but Sessions don't exist yet
exports.getSettings = function (cb) {
  if (!this.settings) {
    return cb(new UnknownError('No settings object for session'));
  }

  return cb(null, {
    status: status.codes.Success.code
  , value: this.settings._settings
  });
};

// settings passed in update matching keys in existing settings object, create if not present.
exports.updateSettings = function (newSettings, cb) {
  if (!this.settings) {
    return cb(new UnknownError('No settings object for session'));
  }
  this.settings.update(newSettings, function (err) {
    if (err) {
      return cb(new UnknownError('err updating setting: ' + err));
    }
    return cb(null, {
      status: status.codes.Success.code
    , value: null
    });
  });
};
<commit_msg>get rid of unused assignment (fix #3594)<commit_after>"use strict";

var errors = require('../server/errors.js')
  , request = require('request')
  , _ = require('underscore')
  , exec = require('child_process').exec
  , status = require("../server/status.js")
  , logger = require('../server/logger.js').get('appium')
  , logDeprecationWarning = require('../helpers.js').logDeprecationWarning;

var UnknownError = errors.UnknownError
  , ProtocolError = errors.ProtocolError;

exports.respond = function (response, cb) {
  if (typeof response === 'undefined') {
    cb(null, '');
  } else {
    if (typeof(response) !== "object") {
      cb(new UnknownError(), response);
    } else if (!('status' in response)) {
      cb(new ProtocolError('Status missing in response from device'), response);
    } else {
      var status = parseInt(response.status, 10);
      if (isNaN(status)) {
        cb(new ProtocolError('Invalid status in response from device'), response);
      } else {
        response.status = status;
        cb(null, response);
      }
    }
  }
};

exports.proxy = function (command, cb) {
  logger.debug('Pushing command to appium work queue: ' + JSON.stringify(command));
  this.push([command, cb]);
};

exports.proxyWithMinTime = function (command, ms, cb) {
  var start = Date.now();
  logger.debug('Pushing command to appium work queue: ' + JSON.stringify(command));
  this.push([command, function () {
    var args = Array.prototype.slice.call(arguments, 0);
    var waitNeeded = ms - (Date.now() - start);
    if (waitNeeded > 0) {
      setTimeout(function () {
        cb.apply(null, args);
      }, waitNeeded);
    } else {
      cb.apply(null, args);
    }
  }]);
};

exports.resetTimeout = function () {
  if (this.onResetTimeout) this.onResetTimeout();
};

exports.waitForCondition = function (waitMs, condFn, cb, intervalMs) {
  if (typeof intervalMs === "undefined") {
    intervalMs = 500;
  }
  var begunAt = Date.now();
  var endAt = begunAt + waitMs;
  var spin = function () {
    condFn(function (condMet) {
      var now = Date.now();
      var waited = now - begunAt;
      var args = Array.prototype.slice.call(arguments);
      if (condMet) {
        cb.apply(this, args.slice(1));
      } else if (now < endAt) {
        logger.debug("Waited for " + waited + "ms so far");
        setTimeout(spin, intervalMs);
      } else {
        logger.debug("Condition unmet after " + waited + "ms. Timing out.");
        cb.apply(this, args.slice(1));
      }
    }.bind(this));
  }.bind(this);
  spin();
};

exports.implicitWaitForCondition = function (condFn, cb) {
  logger.debug("Waiting up to " + this.implicitWaitMs + "ms for condition");
  var _condFn = condFn;
  condFn = function () {
    var args = Array.prototype.slice.call(arguments, 0);
    this.resetTimeout();
    _condFn.apply(this, args);
  }.bind(this);
  this.waitForCondition(this.implicitWaitMs, condFn, cb);
};

exports.doRequest = function (url, method, body, contentType, cb) {
  if (typeof cb === "undefined" && typeof contentType === "function") {
    cb = contentType;
    contentType = null;
  }
  if (typeof contentType === "undefined" || contentType === null) {
    contentType = "application/json;charset=UTF-8";
  }
  if (!(/^https?:\/\//.exec(url))) {
    url = 'http://' + url;
  }
  var opts = {
    url: url
  , method: method
  };
  if (_.contains(['put', 'post', 'patch'], method.toLowerCase())) {
    if (typeof body === "object") {
      opts.json = body;
    } else {
      opts.body = body || "";
    }
  }
  // explicitly set these headers with correct capitalization to work around
  // an issue in node/requests
  logger.debug("Making http request with opts: " + JSON.stringify(opts));
  request(opts, function (err, res, body) {
    cb(err, res, body);
  });
};

exports.isAppInstalled = function (isInstalledCommand, cb) {
  logger.debug("Checking app install status using: " + isInstalledCommand);
  exec(isInstalledCommand, function (error, stdout) {
    cb(error, stdout);
  });
};

exports.removeApp = function (removeCommand, udid, bundleId, cb) {
  logger.debug("Removing app using cmd: " + removeCommand);
  exec(removeCommand, function (error) {
    if (error !== null) {
      cb(new Error('Unable to un-install [' + bundleId + '] from device with id [' + udid + ']. Error [' + error + ']'));
    } else {
      cb(error, 'Successfully un-installed [' + bundleId + '] from device with id [' + udid + ']');
    }
  });
};

exports.installApp = function (installationCommand, udid, unzippedAppPath, cb) {
  logger.debug("Installing app using cmd: " + installationCommand);
  exec(installationCommand, { maxBuffer: 524288 }, function (error) {
    if (error !== null) {
      cb(new Error('Unable to install [' + unzippedAppPath + '] to device with id [' + udid + ']. Error [' + error + ']'));
    } else {
      cb(error, 'Successfully unzipped and installed [' + unzippedAppPath + '] to device with id [' + udid + ']');
    }
  });
};

exports.unpackApp = function (req, packageExtension, cb) {
  var reqAppPath = req.body.appPath;
  if (reqAppPath.toLowerCase().substring(0, 4) === "http") {
    req.device.downloadAndUnzipApp(reqAppPath, function (err, appPath) {
      cb(appPath);
    });
  } else if (reqAppPath.toLowerCase().substring(reqAppPath.length - 4) === ".zip") {
    req.device.unzipLocalApp(reqAppPath, function (err, appPath) {
      cb(appPath);
    });
  } else if (reqAppPath.toLowerCase().substring(reqAppPath.length - 4) === packageExtension) {
    cb(reqAppPath.toString());
  } else {
    cb(null);
  }
};

exports.proxyTo = function (endpoint, method, data, cb) {
  if (endpoint[0] !== '/') {
    endpoint = '/' + endpoint;
  }
  var url = 'http://' + this.proxyHost + ':' + this.proxyPort + endpoint;
  exports.doRequest(url, method, data ? data : '', cb);
};

exports.parseExecuteResponse = function (response, cb) {
  if ((response.value !== null) && (typeof response.value !== "undefined")) {
    var wdElement = null;
    if (!_.isArray(response.value)) {
      if (typeof response.value.ELEMENT !== "undefined") {
        wdElement = this.parseElementResponse(response.value);
        if (wdElement === null) {
          cb(null, {
            status: status.codes.UnknownError.code
          , value: "Error converting element ID atom for using in WD: " + response.value.ELEMENT
          });
        }
        response.value = wdElement;
      }
    } else {
      var args = response.value;
      for (var i = 0; i < args.length; i++) {
        wdElement = args[i];
        if ((args[i] !== null) && (typeof args[i].ELEMENT !== "undefined")) {
          wdElement = this.parseElementResponse(args[i]);
          if (wdElement === null) {
            cb(null, {
              status: status.codes.UnknownError.code
            , value: "Error converting element ID atom for using in WD: " + args[i].ELEMENT
            });
            return;
          }
          args[i] = wdElement;
        }
      }
      response.value = args;
    }
  }
  return response;
};

exports.checkValidLocStrat = function (strat, includeWeb, cb) {
  if (typeof includeWeb === "undefined") {
    includeWeb = false;
  }
  var validStrats = [
    'xpath',
    'id',
    'name',
    'class name'
  ];
  var nativeStrats = [
    '-ios uiautomation',
    'accessibility id',
    '-android uiautomator'
  ];
  var webStrats = [
    'link text',
    'css selector',
    'tag name',
    'partial link text'
  ];
  var nativeDeprecations = {};
  var webDeprecations = {};
  var deprecations = {name: 'accessibility id'};

  if (includeWeb) {
    validStrats = validStrats.concat(webStrats);
    deprecations = _.extend(deprecations, webDeprecations);
  } else {
    validStrats = validStrats.concat(nativeStrats);
    deprecations = _.extend(deprecations, nativeDeprecations);
  }
  if (!_.contains(validStrats, strat)) {
    logger.debug("Invalid locator strategy: " + strat);
    cb(null, {
      status: status.codes.UnknownCommand.code,
      value: {message: "Invalid locator strategy: " + strat}
    });
    return false;
  } else {
    if (_.has(deprecations, strat)) {
      logDeprecationWarning('locator strategy', strat, deprecations[strat]);
    }
    return true;
  }
};

exports.parseElementResponse = function (element) {
  var objId = element.ELEMENT
    , clientId = (5000 + this.webElementIds.length).toString();
  this.webElementIds.push(objId);
  return {ELEMENT: clientId};
};

exports.getAtomsElement = function (wdId) {
  var atomsId;
  try {
    atomsId = this.webElementIds[parseInt(wdId, 10) - 5000];
  } catch (e) {
    return null;
  }
  if (typeof atomsId === "undefined") {
    return null;
  }
  return {'ELEMENT': atomsId};
};

exports.useAtomsElement = function (elementId, failCb, cb) {
  if (parseInt(elementId, 10) < 5000) {
    logger.debug("Element with id " + elementId + " passed in for use with " +
                "atoms, but it's out of our internal scope. Adding 5000");
    elementId = (parseInt(elementId, 10) + 5000).toString();
  }
  var atomsElement = this.getAtomsElement(elementId);
  if (atomsElement === null) {
    failCb(null, {
      status: status.codes.UnknownError.code
    , value: "Error converting element ID for using in WD atoms: " + elementId
    });
  } else {
    cb(atomsElement);
  }
};

exports.convertElementForAtoms = function (args, cb) {
  for (var i = 0; i < args.length; i++) {
    if (args[i] !== null && typeof args[i].ELEMENT !== "undefined") {
      var atomsElement = this.getAtomsElement(args[i].ELEMENT);
      if (atomsElement === null) {
        cb(true, {
          status: status.codes.UnknownError.code
        , value: "Error converting element ID for using in WD atoms: " + args[i].ELEMENT
        });
        return;
      }
      args[i] = atomsElement;
    }
  }
  cb(null, args);
};

exports.jwpError = function (err, code, cb) {
  if (typeof code === "function") {
    cb = code;
    code = null;
  }
  if (code) {
    return cb(null, {
      status: code
    , value: err.message
    });
  }
  return cb(err);
};

exports.jwpSuccess = function (val, cb) {
  if (typeof val === "function") {
    cb = val;
    val = null;
  }
  return cb(null, {
    status: status.codes.Success.code
  , value: val
  });
};

exports.jwpResponse = function (err, val, cb) {
  if (typeof err === "function") {
    return exports.jwpSuccess(err);
  }
  if (typeof val === "function") {
    cb = val;
    val = null;
  }
  if (err) {
    return exports.jwpError(err, status.codes.UnknownError.code, cb);
  }
  return exports.jwpSuccess(val, cb);
};

// methods for appium session Settings
// These should really be on a Session object instead of the Device, but Sessions don't exist yet
exports.getSettings = function (cb) {
  if (!this.settings) {
    return cb(new UnknownError('No settings object for session'));
  }

  return cb(null, {
    status: status.codes.Success.code
  , value: this.settings._settings
  });
};

// settings passed in update matching keys in existing settings object, create if not present.
exports.updateSettings = function (newSettings, cb) {
  if (!this.settings) {
    return cb(new UnknownError('No settings object for session'));
  }
  this.settings.update(newSettings, function (err) {
    if (err) {
      return cb(new UnknownError('err updating setting: ' + err));
    }
    return cb(null, {
      status: status.codes.Success.code
    , value: null
    });
  });
};
<|endoftext|>


<commit_before>const EmailService = require('../lib/email-service')
const sinon = require('sinon')
const expect = require('chai').expect

describe('Email Service', function () {
  let email, transport

  beforeEach(() => {
    transport = {
      name: 'testsend',
      version: '1',
      send: function (data, callback) {
        callback();
      },
      logger: false
    }
    email = new EmailService(transport)
  })

  it('should send emails', (done) => {
    sinon.stub(transport, 'send').yields(null, 'bep bop')

    email.sendMail({
      subject: 'test'
    }, function (err, info) {
      expect(err).to.not.exist;
      expect(info).to.equal('bep bop')
      done()
    })
  })
})
<commit_msg>removing extra semicolons<commit_after>const EmailService = require('../lib/email-service')
const sinon = require('sinon')
const expect = require('chai').expect

describe('Email Service', function () {
  let email, transport

  beforeEach(() => {
    transport = {
      name: 'testsend',
      version: '1',
      send: function (data, callback) {
        callback()
      },
      logger: false
    }
    email = new EmailService(transport)
  })

  it('should send emails', (done) => {
    sinon.stub(transport, 'send').yields(null, 'bep bop')

    email.sendMail({
      subject: 'test'
    }, function (err, info) {
      expect(err).to.not.exist
      expect(info).to.equal('bep bop')
      done()
    })
  })
})
<|endoftext|>


<commit_before>var assert = require('assert');
var request = require('request');
var mongodb = require('mongodb');

/**
 *  This module exports a single function which takes an instance of connect
 *  (or Express) and returns a `MongoDBStore` class that can be used to
 *  store sessions in MongoDB.
 */
describe('MongoDBStore', function() {
  var underlyingDb;
  var server;

  beforeEach(function(done) {
    mongodb.MongoClient.connect(
      'mongodb://localhost:27017/connect_mongodb_session_test',
      function(error, client) {
        if (error) {
          return done(error);
        }
        underlyingDb = client.db('connect_mongodb_session_test');
        client.db('connect_mongodb_session_test').collection('mySessions').deleteMany({}, function(error) {
          return done(error);
        });
      });
  });

  afterEach(function() {
    server.close();
  });

  /**
   *  If you pass in an instance of the
   *  [`express-session` module](http://npmjs.org/package/express-session)
   *  the MongoDBStore class will enable you to store your Express sessions
   *  in MongoDB.
   *
   *  The MongoDBStore class has 3 required options:
   *
   *  1. `uri`: a [MongoDB connection string](http://docs.mongodb.org/manual/reference/connection-string/)
   *  2. `databaseName`: the MongoDB database to store sessions in
   *  3. `collection`: the MongoDB collection to store sessions in
   *
   *  **Note:** You can pass a callback to the `MongoDBStore` constructor,
   *  but this is entirely optional. The Express 3.x example demonstrates
   *  that you can use the MongoDBStore class in a synchronous-like style: the
   *  module will manage the internal connection state for you.
   */
  it('can store sessions for Express 4', function(done) {
    var express = require('express');
    var session = require('express-session');
    var MongoDBStore = require('connect-mongodb-session')(session);

    var app = express();
    var store = new MongoDBStore({
      uri: 'mongodb://localhost:27017/connect_mongodb_session_test',
      collection: 'mySessions'
    });
    // acquit:ignore:start

    store.on('connected', function() {
      store.client; // The underlying MongoClient object from the MongoDB driver
      assert.ok(store.client);
      assert.ok(store.db);
    });
    // acquit:ignore:end

    // Catch errors
    store.on('error', function(error) {
      assert.ifError(error);
      assert.ok(false);
    });

    app.use(require('express-session')({
      secret: 'This is a secret',
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week
      },
      store: store,
      // Boilerplate options, see:
      // * https://www.npmjs.com/package/express-session#resave
      // * https://www.npmjs.com/package/express-session#saveuninitialized
      resave: true,
      saveUninitialized: true
    }));

    app.get('/', function(req, res) {
      res.send('Hello ' + JSON.stringify(req.session));
    });

    server = app.listen(3000);

    // acquit:ignore:start
    underlyingDb.collection('mySessions').count({}, function(error, count) {
      assert.ifError(error);
      assert.equal(0, count);

      request('http://localhost:3000', function(error, response, body) {
        assert.ifError(error);
        assert.equal(1, response.headers['set-cookie'].length);
        var cookie = require('cookie').parse(response.headers['set-cookie'][0]);
        assert.ok(cookie['connect.sid']);
        underlyingDb.collection('mySessions').count({}, function(error, count) {
          assert.ifError(error);
          assert.equal(1, count);
          var config = {
            url: 'http://localhost:3000',
            headers: { 'Cookie': 'connect.sid=' + cookie['connect.sid'] }
          };
          request(config, function(error, response, body) {
            assert.ok(!response.headers['set-cookie']);
            store.clear(function(error) {
              assert.ifError(error);
              underlyingDb.collection('mySessions').count({}, function(error, count) {
                assert.ifError(error);
                assert.equal(0, count);
                done();
              });
            });
          });
        });
      });
    });
    // acquit:ignore:end
  });

  /**
   *  You should pass a callback to the `MongoDBStore` constructor to catch
   *  errors. If you don't pass a callback to the `MongoDBStore` constructor,
   *  `MongoDBStore` will `throw` if it can't connect.
   */
  it('throws an error when it can\'t connect to MongoDB', function(done) {
    var express = require('express');
    var session = require('express-session');
    var MongoDBStore = require('connect-mongodb-session')(session);

    var app = express();
    var numExpectedSources = 2;
    var store = new MongoDBStore(
      {
        uri: 'mongodb://bad.host:27000/connect_mongodb_session_test?connectTimeoutMS=10',
        databaseName: 'connect_mongodb_session_test',
        collection: 'mySessions'
      },
      function(error) {
        // Should have gotten an error
        // acquit:ignore:start
        assert.ok(error);
        --numExpectedSources || done();
        // acquit:ignore:end
      });

    store.on('error', function(error) {
      // Also get an error here
      // acquit:ignore:start
      assert.ok(error);
      --numExpectedSources || done();
      // acquit:ignore:end
    });

    app.use(session({
      secret: 'This is a secret',
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week
      },
      store: store,
      // Boilerplate options, see:
      // * https://www.npmjs.com/package/express-session#resave
      // * https://www.npmjs.com/package/express-session#saveuninitialized
      resave: true,
      saveUninitialized: true
    }));

    app.get('/', function(req, res) {
      res.send('Hello ' + JSON.stringify(req.session));
    });

    server = app.listen(3000);
  });
});
<commit_msg>Replace assert with console.log <commit_after>var assert = require('assert');
var request = require('request');
var mongodb = require('mongodb');

/**
 *  This module exports a single function which takes an instance of connect
 *  (or Express) and returns a `MongoDBStore` class that can be used to
 *  store sessions in MongoDB.
 */
describe('MongoDBStore', function() {
  var underlyingDb;
  var server;

  beforeEach(function(done) {
    mongodb.MongoClient.connect(
      'mongodb://localhost:27017/connect_mongodb_session_test',
      function(error, client) {
        if (error) {
          return done(error);
        }
        underlyingDb = client.db('connect_mongodb_session_test');
        client.db('connect_mongodb_session_test').collection('mySessions').deleteMany({}, function(error) {
          return done(error);
        });
      });
  });

  afterEach(function() {
    server.close();
  });

  /**
   *  If you pass in an instance of the
   *  [`express-session` module](http://npmjs.org/package/express-session)
   *  the MongoDBStore class will enable you to store your Express sessions
   *  in MongoDB.
   *
   *  The MongoDBStore class has 3 required options:
   *
   *  1. `uri`: a [MongoDB connection string](http://docs.mongodb.org/manual/reference/connection-string/)
   *  2. `databaseName`: the MongoDB database to store sessions in
   *  3. `collection`: the MongoDB collection to store sessions in
   *
   *  **Note:** You can pass a callback to the `MongoDBStore` constructor,
   *  but this is entirely optional. The Express 3.x example demonstrates
   *  that you can use the MongoDBStore class in a synchronous-like style: the
   *  module will manage the internal connection state for you.
   */
  it('can store sessions for Express 4', function(done) {
    var express = require('express');
    var session = require('express-session');
    var MongoDBStore = require('connect-mongodb-session')(session);

    var app = express();
    var store = new MongoDBStore({
      uri: 'mongodb://localhost:27017/connect_mongodb_session_test',
      collection: 'mySessions'
    });
    // acquit:ignore:start

    store.on('connected', function() {
      store.client; // The underlying MongoClient object from the MongoDB driver
      assert.ok(store.client);
      assert.ok(store.db);
    });
    // acquit:ignore:end

    // Catch errors
    store.on('error', function(error) {
      console.log(error),
    });

    app.use(require('express-session')({
      secret: 'This is a secret',
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week
      },
      store: store,
      // Boilerplate options, see:
      // * https://www.npmjs.com/package/express-session#resave
      // * https://www.npmjs.com/package/express-session#saveuninitialized
      resave: true,
      saveUninitialized: true
    }));

    app.get('/', function(req, res) {
      res.send('Hello ' + JSON.stringify(req.session));
    });

    server = app.listen(3000);

    // acquit:ignore:start
    underlyingDb.collection('mySessions').count({}, function(error, count) {
      assert.ifError(error);
      assert.equal(0, count);

      request('http://localhost:3000', function(error, response, body) {
        assert.ifError(error);
        assert.equal(1, response.headers['set-cookie'].length);
        var cookie = require('cookie').parse(response.headers['set-cookie'][0]);
        assert.ok(cookie['connect.sid']);
        underlyingDb.collection('mySessions').count({}, function(error, count) {
          assert.ifError(error);
          assert.equal(1, count);
          var config = {
            url: 'http://localhost:3000',
            headers: { 'Cookie': 'connect.sid=' + cookie['connect.sid'] }
          };
          request(config, function(error, response, body) {
            assert.ok(!response.headers['set-cookie']);
            store.clear(function(error) {
              assert.ifError(error);
              underlyingDb.collection('mySessions').count({}, function(error, count) {
                assert.ifError(error);
                assert.equal(0, count);
                done();
              });
            });
          });
        });
      });
    });
    // acquit:ignore:end
  });

  /**
   *  You should pass a callback to the `MongoDBStore` constructor to catch
   *  errors. If you don't pass a callback to the `MongoDBStore` constructor,
   *  `MongoDBStore` will `throw` if it can't connect.
   */
  it('throws an error when it can\'t connect to MongoDB', function(done) {
    var express = require('express');
    var session = require('express-session');
    var MongoDBStore = require('connect-mongodb-session')(session);

    var app = express();
    var numExpectedSources = 2;
    var store = new MongoDBStore(
      {
        uri: 'mongodb://bad.host:27000/connect_mongodb_session_test?connectTimeoutMS=10',
        databaseName: 'connect_mongodb_session_test',
        collection: 'mySessions'
      },
      function(error) {
        // Should have gotten an error
        // acquit:ignore:start
        assert.ok(error);
        --numExpectedSources || done();
        // acquit:ignore:end
      });

    store.on('error', function(error) {
      // Also get an error here
      // acquit:ignore:start
      assert.ok(error);
      --numExpectedSources || done();
      // acquit:ignore:end
    });

    app.use(session({
      secret: 'This is a secret',
      cookie: {
        maxAge: 1000 * 60 * 60 * 24 * 7 // 1 week
      },
      store: store,
      // Boilerplate options, see:
      // * https://www.npmjs.com/package/express-session#resave
      // * https://www.npmjs.com/package/express-session#saveuninitialized
      resave: true,
      saveUninitialized: true
    }));

    app.get('/', function(req, res) {
      res.send('Hello ' + JSON.stringify(req.session));
    });

    server = app.listen(3000);
  });
});
<|endoftext|>


<commit_before>/*jslint node:true, browser:true */

'use strict';

var Output = require('./output');
var promise = require('es6-promise');

/**
 * Connect to the first available MIDI output
 *
 * Will fail if no output is connected or if
 * access to MIDI driver is denied by the user.
 * (In node, access is always granted.)
 *
 * @return Promise
 */
function connect() {
    return new promise.Promise(function (resolve, reject) {
        var driver, output;

        navigator.requestMIDIAccess().then(function (access) {
            driver = access.outputs;

            if (driver.size) {
                driver = driver.get(0);
                output = new Output(driver.send);

                resolve(output);
            } else {
                reject(new Error('No available MIDI output'));
            }
        }).catch(function () {
            reject(new Error('MIDI access denied'));
        });
    });
}

if (global.navigator === undefined) {
    module.exports = undefined;
} else {
    module.exports = connect;
}<commit_msg>:bulb: Use driver wrapper in connect()<commit_after>/*jslint node:true, browser:true */

'use strict';

var Driver = require('./driver').Driver;
var promise = require('es6-promise');

/**
 * Establish connection to MIDI driver
 *
 * Will fail if access to MIDI driver is denied by the user.
 *
 * @return Promise
 */
function connect() {
    return new promise.Promise(function (resolve, reject) {
        navigator.requestMIDIAccess().then(function (access) {
            resolve(new Driver(access));
        }).catch(function () {
            reject(new Error('MIDI access denied'));
        });
    });
}

if (global.navigator === undefined) {
    module.exports = undefined;
} else {
    module.exports = connect;
}<|endoftext|>


<commit_before>/* eslint-disable */ // (TO BE REWORKED SOON)

const snekfetch = require('snekfetch');
const fs        = require('fs');

exports.create = (guild) => {
    prefixdb[guild.id] = settings.defaultPrefix;
    fs.writeFile('./storage/prefixdb.json', JSON.stringify(prefixdb, '', '\t'), (err) => {
        if (err)
            return console.log(`${Date()} createGuildHandler error: ${err}`);
    });
    guild.defaultChannel.send(`Hi, I'm ${client.user.username} and I give you the possibility to set reminders and view or delete them.
    To see a list of my commands, send \`${prefixdb[guild.id]}help\`.\nFeel free to DM Aetheryx#2222 for any questions or concerns!`);
    postStats();
};

exports.delete = (guild) => {
    delete prefixdb[guild.id];
    fs.writeFile('./storage/prefixdb.json', JSON.stringify(prefixdb, '', '\t'), (err) => {
        if (err)
            return console.log(`${Date()} deleteGuildHandler error: ${err}`);
    });
    postStats();
};

function postStats () {
    if (!settings.keys.botspw.includes('API key'))
        snekfetch
            .post(`https://bots.discord.pw/api/bots/${client.user.id}/stats`)
            .set('Authorization', settings.keys.botspw)
            .send({ server_count: client.guilds.size })
            .end();

    if (!settings.keys.dbots.includes('API key'))
        snekfetch
            .post(`https://discordbots.org/api/bots/${client.user.id}/stats`)
            .set('Authorization', settings.keys.dbots)
            .send({ server_count: client.guilds.size })
            .end();
}
<commit_msg>Add guildHandlers<commit_after>const snekfetch = require('snekfetch');

exports.create = (Bot, guild) => {
    guild.owner.send(`Thanks for adding me to your server! To see a list of my commands, send \`${Bot.config.defaultPrefix}help\`.\nFeel free to DM Aetheryx#2222 for any questions or concerns!`);
    postStats(Bot);
};

exports.delete = async (Bot, guild) => {
    await Bot.db.run('DELETE FROM prefixes WHERE guildID = ?;', guild.id);
    Bot.prefixes.delete(guild.id);
    postStats(Bot);
};

function postStats (Bot) {
    if (Bot.config.keys.botspw) {
        snekfetch
            .post(`https://bots.discord.pw/api/bots/${Bot.client.user.id}/stats`)
            .set('Authorization', Bot.config.keys.botspw)
            .send({ server_count: Bot.client.guilds.size })
            .end();
    }

    if (Bot.config.keys.dbots) {
        snekfetch
            .post(`https://discordbots.org/api/bots/${Bot.client.user.id}/stats`)
            .set('Authorization', Bot.config.keys.dbots)
            .send({ server_count: Bot.client.guilds.size })
            .end();
    }
}
<|endoftext|>