<commit_before>'use strict';

var Promise = require('bluebird');
var _       = require('lodash');
var Client  = require('./client');
var Kafka   = require('./index');
var errors  = require('./errors');

function BaseConsumer(options) {
    this.options = _.defaultsDeep(options || {}, {
        timeout: 100, // client timeout for produce and fetch requests
        idleTimeout: 1000, // timeout between fetch requests
        minBytes: 1,
        maxBytes: 1024 * 1024,
        recoveryOffset: Kafka.LATEST_OFFSET
    });

    this.client = new Client(this.options);

    this.subscriptions = {};

    this._finished = false;
}

module.exports = BaseConsumer;

/**
 * Initialize BaseConsumer
 *
 * @return {Prommise}
 */
BaseConsumer.prototype.init = function () {
    this._fetchPromise = this._fetch();
    return this.client.init();
};

BaseConsumer.prototype._fetch = function () {
    var self = this;

    return Promise.try(function () {
        var data = _(self.subscriptions).reject({ paused: true }).values().groupBy('leader').mapValues(function (v) {
            return _(v)
                .groupBy('topic')
                .map(function (p, t) {
                    return {
                        topicName: t,
                        partitions: p
                    };
                })
                .value();
        }).value();

        if (_.isEmpty(data)) {
            return null;
        }

        return self.client.fetchRequest(data).then(function (results) {
            if (self._finished) { return null; }
            return Promise.map(results, function (p) {
                var s = self.subscriptions[p.topic + ':' + p.partition];
                if (!s) {
                    return null; // already unsubscribed while we were polling
                }
                if (p.error) {
                    if (p.error.code === 'OffsetOutOfRange') { // update partition offset to latest
                        self.client.warn('Updating offset because of OffsetOutOfRange error for', p.topic + ':' + p.partition);
                        return self._offset(s.leader, p.topic, p.partition, null, self.options.recoveryOffset).then(function (offset) {
                            s.offset = offset;
                        });
                    } else if (/UnknownTopicOrPartition|NotLeaderForPartition|LeaderNotAvailable/.test(p.error.code)) {
                        self.client.debug('Received', p.error.code, 'error for', p.topic + ':' + p.partition);
                        return self._updateSubscription(p.topic, p.partition);
                    } else if (p.error instanceof errors.NoKafkaConnectionError) {
                        self.client.debug('Received connection error for', p.topic + ':' + p.partition);
                        return self._updateSubscription(p.topic, p.partition);
                    }
                    self.client.error(p.topic + ':' + p.partition, p.error);
                } else if (p.messageSet.length) {
                    s.paused = true;
                    s.handler(p.messageSet, p.topic, p.partition, p.highwaterMarkOffset)
                    .catch(function (err) {
                        self.client.warn('Handler for', p.topic + ':' + p.partition, 'failed with', err);
                    })
                    .finally(function () {
                        s.paused = false;
                        s.offset = _.last(p.messageSet).offset + 1; // advance offset position
                    });
                }
                return null;
            });
        });
    })
    .catch(function (err) {
        if (!self._finished) {
            self.client.error(err);
        }
    })
    .then(function () {
        if (!self._finished) {
            return Promise.delay(self.options.idleTimeout).then(function () {
                if (!self._finished) {
                    return self._fetch();
                }
            });
        }
    });
};

BaseConsumer.prototype._updateSubscription = function (topic, partition) {
    var self = this;

    return self.client.updateMetadata().then(function () {
        var s = self.subscriptions[topic + ':' + partition];

        return self.subscribe(topic, partition, s.offset ? { offset: s.offset } : s.options, s.handler)
        .catch({ code: 'LeaderNotAvailable' }, function () {}) // ignore and try again on next fetch
        .catch(function (err) {
            self.client.error('Failed to re-subscribe to', topic + ':' + partition, err);
        });
    });
};

BaseConsumer.prototype._offset = function (leader, topic, partition, time) {
    var self = this, request = {};

    request[leader] = [{
        topicName: topic,
        partitions: [{
            partition: partition,
            time: time || Kafka.LATEST_OFFSET, // the latest (next) offset by default
            maxNumberOfOffsets: 1
        }]
    }];

    return self.client.offsetRequest(request).then(function (result) {
        var p = result[0];
        if (p.error) {
            throw p.error;
        }
        return p.offset[0];
    });
};

/**
 * Get offset for specified topic/partition and time
 *
 * @param  {String} topic
 * @param  {Number} partition
 * @param  {Number} time      Time in ms or two specific values: Kafka.EARLIEST_OFFSET and Kafka.LATEST_OFFSET
 * @return {Promise}
 */
BaseConsumer.prototype.offset = function (topic, partition, time) {
    var self = this;

    partition = partition || 0;

    return self.client.findLeader(topic, partition).then(function (leader) {
        return self._offset(leader, topic, partition, time);
    });
};

/**
 * Subscribe to topic/partition
 * @param  {String} topic
 * @param  {Number} [partition]
 * @param  {Object} [options] { maxBytes, offset, time }
 * @param  {Function} handler
 * @return {Promise}
 */
BaseConsumer.prototype.subscribe = function (topic, partition, options, handler) {
    var self = this;

    if (typeof partition === 'function') {
        handler = partition;
        partition = 0;
        options = {};
    } else if (typeof options === 'function') {
        handler = options;
        options = {};
    }

    function _subscribe(leader, _offset) {
        self.client.debug('Subscribed to', topic + ':' + partition);
        self.subscriptions[topic + ':' + partition] = {
            topic: topic,
            partition: partition,
            offset: _offset,
            leader: leader,
            maxBytes: options.maxBytes || self.options.maxBytes,
            handler: handler
        };
        return _offset;
    }

    return self.client.findLeader(topic, partition).then(function (leader) {
        handler = Promise.method(handler);

        if (options.offset >= 0) {
            return _subscribe(leader, options.offset);
        }
        return self._offset(leader, topic, partition, options.time).then(function (offset) {
            return _subscribe(leader, offset);
        });
    })
    .catch({ code: 'LeaderNotAvailable' }, function () {
        // these subscriptions will be retried on each _fetch()
        self.subscriptions[topic + ':' + partition] = {
            topic: topic,
            partition: partition,
            options: options,
            leader: -1,
            handler: handler
        };
    });
};

/**
 * Unsubscribe from topic/partition
 *
 * @param  {String} topic
 * @param  {Number} partition
 * @return {Promise}
 */
BaseConsumer.prototype.unsubscribe = function (topic, partition) {
    var self = this;

    partition = partition || 0;

    return new Promise(function (resolve) {
        delete self.subscriptions[topic + ':' + partition];
        return resolve();
    });
};

BaseConsumer.prototype.end = function () {
    var self = this;

    self._finished = true;
    self.subscriptions = {};

    return self._fetchPromise.then(function () {
        return self.client.end();
    });
};
<commit_msg>Split fetch code<commit_after>'use strict';

var Promise = require('bluebird');
var _       = require('lodash');
var Client  = require('./client');
var Kafka   = require('./index');
var errors  = require('./errors');

function BaseConsumer(options) {
    this.options = _.defaultsDeep(options || {}, {
        timeout: 100, // client timeout for produce and fetch requests
        idleTimeout: 1000, // timeout between fetch requests
        minBytes: 1,
        maxBytes: 1024 * 1024,
        recoveryOffset: Kafka.LATEST_OFFSET
    });

    this.client = new Client(this.options);

    this.subscriptions = {};

    this._finished = false;
}

module.exports = BaseConsumer;

/**
 * Initialize BaseConsumer
 *
 * @return {Prommise}
 */
BaseConsumer.prototype.init = function () {
    this._fetchPromise = this._fetch();
    return this.client.init();
};

BaseConsumer.prototype._fetch = function () {
    var self = this;

    return Promise.try(function () {
        var data = _(self.subscriptions).reject({ paused: true }).values().groupBy('leader').mapValues(function (v) {
            return _(v)
                .groupBy('topic')
                .map(function (p, t) {
                    return {
                        topicName: t,
                        partitions: p
                    };
                })
                .value();
        }).value();

        if (_.isEmpty(data)) {
            return null;
        }

        return self.client.fetchRequest(data).then(function (results) {
            if (self._finished) { return null; }
            return Promise.map(results, function (p) {
                var s = self.subscriptions[p.topic + ':' + p.partition];
                if (!s) {
                    return null; // already unsubscribed while we were polling
                }
                if (p.error) {
                    return self._partitionError(p.error, p.topic, p.partition);
                } else if (p.messageSet.length) {
                    s.paused = true;
                    s.handler(p.messageSet, p.topic, p.partition, p.highwaterMarkOffset)
                    .catch(function (err) {
                        self.client.warn('Handler for', p.topic + ':' + p.partition, 'failed with', err);
                    })
                    .finally(function () {
                        s.paused = false;
                        s.offset = _.last(p.messageSet).offset + 1; // advance offset position
                    });
                }
                return null;
            });
        });
    })
    .catch(function (err) {
        if (!self._finished) {
            self.client.error(err);
        }
    })
    .then(function () {
        if (!self._finished) {
            return Promise.delay(self.options.idleTimeout).then(function () {
                if (!self._finished) {
                    return self._fetch();
                }
            });
        }
    });
};

BaseConsumer.prototype._partitionError = function (err, topic, partition) {
    var self = this;

    var s = self.subscriptions[topic + ':' + partition];

    if (err.code === 'OffsetOutOfRange') { // update partition offset to options.recoveryOffset
        self.client.warn('Updating offset because of OffsetOutOfRange error for', topic + ':' + partition);
        return self._offset(s.leader, topic, partition, null, self.options.recoveryOffset).then(function (offset) {
            s.offset = offset;
        });
    } else if (/UnknownTopicOrPartition|NotLeaderForPartition|LeaderNotAvailable/.test(err.code)) {
        self.client.debug('Received', err.code, 'error for', topic + ':' + partition);
        return self._updateSubscription(topic, partition);
    } else if (err instanceof errors.NoKafkaConnectionError) {
        self.client.debug('Received connection error for', topic + ':' + partition);
        return self._updateSubscription(topic, partition);
    }
    self.client.error(topic + ':' + partition, err);
};

BaseConsumer.prototype._updateSubscription = function (topic, partition) {
    var self = this;

    return self.client.updateMetadata().then(function () {
        var s = self.subscriptions[topic + ':' + partition];

        return self.subscribe(topic, partition, s.offset ? { offset: s.offset } : s.options, s.handler)
        .catch({ code: 'LeaderNotAvailable' }, function () {}) // ignore and try again on next fetch
        .catch(function (err) {
            self.client.error('Failed to re-subscribe to', topic + ':' + partition, err);
        });
    });
};

BaseConsumer.prototype._offset = function (leader, topic, partition, time) {
    var self = this, request = {};

    request[leader] = [{
        topicName: topic,
        partitions: [{
            partition: partition,
            time: time || Kafka.LATEST_OFFSET, // the latest (next) offset by default
            maxNumberOfOffsets: 1
        }]
    }];

    return self.client.offsetRequest(request).then(function (result) {
        var p = result[0];
        if (p.error) {
            throw p.error;
        }
        return p.offset[0];
    });
};

/**
 * Get offset for specified topic/partition and time
 *
 * @param  {String} topic
 * @param  {Number} partition
 * @param  {Number} time      Time in ms or two specific values: Kafka.EARLIEST_OFFSET and Kafka.LATEST_OFFSET
 * @return {Promise}
 */
BaseConsumer.prototype.offset = function (topic, partition, time) {
    var self = this;

    partition = partition || 0;

    return self.client.findLeader(topic, partition).then(function (leader) {
        return self._offset(leader, topic, partition, time);
    });
};

/**
 * Subscribe to topic/partition
 * @param  {String} topic
 * @param  {Number} [partition]
 * @param  {Object} [options] { maxBytes, offset, time }
 * @param  {Function} handler
 * @return {Promise}
 */
BaseConsumer.prototype.subscribe = function (topic, partition, options, handler) {
    var self = this;

    if (typeof partition === 'function') {
        handler = partition;
        partition = 0;
        options = {};
    } else if (typeof options === 'function') {
        handler = options;
        options = {};
    }

    function _subscribe(leader, _offset) {
        self.client.debug('Subscribed to', topic + ':' + partition);
        self.subscriptions[topic + ':' + partition] = {
            topic: topic,
            partition: partition,
            offset: _offset,
            leader: leader,
            maxBytes: options.maxBytes || self.options.maxBytes,
            handler: handler
        };
        return _offset;
    }

    return self.client.findLeader(topic, partition).then(function (leader) {
        handler = Promise.method(handler);

        if (options.offset >= 0) {
            return _subscribe(leader, options.offset);
        }
        return self._offset(leader, topic, partition, options.time).then(function (offset) {
            return _subscribe(leader, offset);
        });
    })
    .catch({ code: 'LeaderNotAvailable' }, function () {
        // these subscriptions will be retried on each _fetch()
        self.subscriptions[topic + ':' + partition] = {
            topic: topic,
            partition: partition,
            options: options,
            leader: -1,
            handler: handler
        };
    });
};

/**
 * Unsubscribe from topic/partition
 *
 * @param  {String} topic
 * @param  {Number} partition
 * @return {Promise}
 */
BaseConsumer.prototype.unsubscribe = function (topic, partition) {
    var self = this;

    partition = partition || 0;

    return new Promise(function (resolve) {
        delete self.subscriptions[topic + ':' + partition];
        return resolve();
    });
};

BaseConsumer.prototype.end = function () {
    var self = this;

    self._finished = true;
    self.subscriptions = {};

    return self._fetchPromise.then(function () {
        return self.client.end();
    });
};
<|endoftext|>


<commit_before>// Behavior
// --------
//
// Represents a behavior of some element or group of elements.
// The objetive is define a set of rules and events who
// can be associated to an element and reutilized later on
//
// When a behavior is defined, a hash of events must be defined too,
// and on initialization a DOM element must be provided
//
// **Example**
// ```javascript
// Carousel = Essential.Behavior.extend({
//   events: {
//     "click .next": "goToNextSlide"
//   },
//
//   goToNextSlide: function(e) {
//     //...
//   }
// });
//
// var carousel = Carousel.new(domElement);
// ```

Essential.Behavior = Proto.extend({
  constructor: function (domElement) {
    this.el = domElement;
    this.delegateEvents();
    if (typeof this.init === "function") {
      this.init();
    }
  },

  delegateEvents: function () {
    if (typeof this.events === "undefined") {
      return;
    }

    var delegateEventSplitter = /^(\S+)\s*(.*)$/;

    for (var key in this.events) {
      var method = this.events[key];

      var match = key.match(delegateEventSplitter);
      var eventName = match[1],
        selector = match[2],
        nodeList = selector ? this.el.querySelectorAll(selector) : [this.el];

      if (typeof this[method] === "undefined") {
        continue;
      }

      Essential.Core.bind(eventName, nodeList, this[method]);
    }
  }
});
<commit_msg>Adds missing annotated documentation for the Behavior#init function<commit_after>// Behavior
// --------
//
// Represents a behavior of some element or group of elements.
// The objetive is define a set of rules and events who
// can be associated to an element and reutilized later on
//
// When a behavior is defined, a hash of events must be defined too,
// and on initialization a DOM element must be provided
//
// Also you can define an `init` function, who is always called when the
// behavior is initialized
//
// **Example**
// ```javascript
// Carousel = Essential.Behavior.extend({
//   events: {
//     "click .next": "goToNextSlide"
//   },
//
//  init: function() {
//    // Called on behavior initialization
//  },
//
//   goToNextSlide: function(e) {
//     //...
//   }
// });
//
// var carousel = Carousel.new(domElement);
// ```

Essential.Behavior = Proto.extend({
  constructor: function (domElement) {
    this.el = domElement;
    this.delegateEvents();

    if (typeof this.init === "function") {
      this.init();
    }
  },

  delegateEvents: function () {
    if (typeof this.events === "undefined") {
      return;
    }

    var delegateEventSplitter = /^(\S+)\s*(.*)$/;

    for (var key in this.events) {
      var method = this.events[key];

      var match = key.match(delegateEventSplitter);
      var eventName = match[1],
        selector = match[2],
        nodeList = selector ? this.el.querySelectorAll(selector) : [this.el];

      if (typeof this[method] === "undefined") {
        continue;
      }

      Essential.Core.bind(eventName, nodeList, this[method]);
    }
  }
});
<|endoftext|>


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

'use strict'

// isGlobal has no use, make eslint happy
// but eslint complains for vars-on-top
// so ...
var isGlobal = require('./is-global') // eslint-disable-line no-unused-vars

var options = {
  getCurrentRules: ['current', 'c'],
  getPluginRules: ['plugin', 'p'],
  getAllAvailableRules: ['all-available', 'a'],
  getUnusedRules: ['unused', 'u'],
  n: ['no-error'],
  verbose: ['verbose', 'v'],
}

var argv = require('yargs')
  .boolean(Object.keys(options))
  .alias(options)
  .argv

var cli = require('../lib/cli-util')

var processExitCode = argv.u && !argv.n ? 1 : 0
var getRuleFinder = require('../lib/rule-finder')
var specifiedFile = argv._[0]

var ruleFinder = getRuleFinder(specifiedFile)

if (!argv.c && !argv.p && !argv.a && !argv.u) {
  console.log('no option provided, please provide a valid option') // eslint-disable-line no-console
  console.log('usage:') // eslint-disable-line no-console
  console.log('eslint-find-rules [option] <file> [flag]') // eslint-disable-line no-console
  process.exit(0)
}

Object.keys(options).forEach(function findRules(option) {
  var rules
  var ruleFinderMethod = ruleFinder[option]
  if (argv[option] && ruleFinderMethod) {
    rules = ruleFinderMethod()
    argv.verbose && cli.push('\n' + options[option][0] + ' rules\n' + rules.length + ' rules found\n')
    if (rules.length) {
      !argv.verbose && cli.push('\n' + options[option][0] + ' rules\n')
      cli.push(rules)
      cli.write()
    } else /* istanbul ignore else */ if (option === 'getUnusedRules') {
      processExitCode = 0
    }
  }
})

if (processExitCode) {
  process.exit(processExitCode)
}
<commit_msg>fix(version): skip is-global check (#84)<commit_after>#!/usr/bin/env node

'use strict'

// isGlobal has no use, make eslint happy
// but eslint complains for vars-on-top
// so ...
// var isGlobal = require('./is-global') // eslint-disable-line no-unused-vars

var options = {
  getCurrentRules: ['current', 'c'],
  getPluginRules: ['plugin', 'p'],
  getAllAvailableRules: ['all-available', 'a'],
  getUnusedRules: ['unused', 'u'],
  n: ['no-error'],
  verbose: ['verbose', 'v'],
}

var argv = require('yargs')
  .boolean(Object.keys(options))
  .alias(options)
  .argv

var cli = require('../lib/cli-util')

var processExitCode = argv.u && !argv.n ? 1 : 0
var getRuleFinder = require('../lib/rule-finder')
var specifiedFile = argv._[0]

var ruleFinder = getRuleFinder(specifiedFile)

if (!argv.c && !argv.p && !argv.a && !argv.u) {
  console.log('no option provided, please provide a valid option') // eslint-disable-line no-console
  console.log('usage:') // eslint-disable-line no-console
  console.log('eslint-find-rules [option] <file> [flag]') // eslint-disable-line no-console
  process.exit(0)
}

Object.keys(options).forEach(function findRules(option) {
  var rules
  var ruleFinderMethod = ruleFinder[option]
  if (argv[option] && ruleFinderMethod) {
    rules = ruleFinderMethod()
    argv.verbose && cli.push('\n' + options[option][0] + ' rules\n' + rules.length + ' rules found\n')
    if (rules.length) {
      !argv.verbose && cli.push('\n' + options[option][0] + ' rules\n')
      cli.push(rules)
      cli.write()
    } else /* istanbul ignore else */ if (option === 'getUnusedRules') {
      processExitCode = 0
    }
  }
})

if (processExitCode) {
  process.exit(processExitCode)
}
<|endoftext|>


<commit_before>'use strict';

const h = require('highland');
const DirectPipelineRunner = require('./DirectPipelineRunner');

class Pipeline {
  constructor(options) {
    options = options || {};
    this._runner = new DirectPipelineRunner(options);
    this._input = h();
    this._transforms = [];
  }

  apply(obj) {

    /**
     * If we have a curried function then save it as is:
     */

    if (typeof obj === 'function') {
      this._transforms.push(obj);
    }

    /**
     * Otherwise, we let the object itself tell us what to
     * save:
     */

    else {
      this._transforms.push(obj.apply());
    }

    return this;
  }

  run(finished) {
    this._runner.run(this, finished);
  }
};

exports.create = (options) => {
  return new Pipeline(options);
}
<commit_msg>quick change to allow pipelines to be used in apply() of another pipeline<commit_after>'use strict';

const h = require('highland');
const DirectPipelineRunner = require('./DirectPipelineRunner');

class Pipeline {
  constructor(options) {
    options = options || {};
    this._runner = new DirectPipelineRunner(options);
    this._input = h();
    this._transforms = [];
  }

  apply(obj) {

    if (!obj) {
      return h.pipeline.apply(null, this._transforms);
    }

    /**
     * If we have a curried function then save it as is:
     */

    if (typeof obj === 'function') {
      this._transforms.push(obj);
    }

    /**
     * Otherwise, we let the object itself tell us what to
     * save:
     */

    else {
      this._transforms.push(obj.apply());
    }

    return this;
  }

  run(finished) {
    this._runner.run(this, finished);
  }
};

exports.create = (options) => {
  return new Pipeline(options);
}
<|endoftext|>


<commit_before>const tape = require('tape')
const sodium = require('sodium-universal')
const collect = require('stream-collector')

const FuzzBuzz = require('fuzzbuzz')
const create = require('./helpers/create')

const MAX_PATH_DEPTH = 30
const MAX_FILE_LENGTH = 1e3
const CHARACTERS = 1e3
const INVALID_CHARS = new Set(['/', '\\', '?', '%', '*', ':', '|', '"', '<', '>', '.', ' '])

class HyperdriveFuzzer extends FuzzBuzz {
  constructor (opts) {
    super(opts)

    this.add(10, this.writeFile)
    this.add(5, this.deleteFile)
    this.add(5, this.existingFileOverwrite)
    this.add(3, this.statFile)
    this.add(3, this.statDirectory)
    this.add(2, this.deleteInvalidFile)
    this.add(2, this.randomReadStream)
    this.add(1, this.writeAndMkdir)
  }

  // START Helper functions.

  _select (map) {
    let idx = this.randomInt(map.size -1)
    if (idx < 0) return null

    let ite = map.entries()
    while (idx--) ite.next()
    return ite.next().value
  }
  _selectFile () {
    return this._select(this.files)
  }
  _selectDirectory () {
    return this._select(this.directories)
  }

  _validChar () {
    do {
      var char = String.fromCharCode(this.randomInt(CHARACTERS))
    } while (INVALID_CHARS.has(char))
    return char
  }
  _fileName () {
    do {
      let depth = Math.max(this.randomInt(MAX_PATH_DEPTH), 1)
      var name = (new Array(depth)).fill(0).map(() => this._validChar()).join('/')
    } while (this.files.get(name) || this.directories.get(name))
    return name
  }
  _createFile () {
    let name = this._fileName()
    let content = Buffer.allocUnsafe(this.randomInt(MAX_FILE_LENGTH)).fill(0).map(() => this.randomInt(10))
    return { name, content }
  }
  _deleteFile (name) {
    return new Promise((resolve, reject) => {
      this.drive.unlink(name, err => {
        if (err) return reject(err)
        this.files.delete(name)
        return resolve({ type: 'delete', name })
      })
    })
  }

  // START FuzzBuzz interface

  _setup () {
    this.drive = create()
    this.files = new Map()
    this.directories = new Map()
    this.streams = new Map()
    this.log = []

    return new Promise((resolve, reject) => {
      this.drive.contentReady(err => {
        if (err) return reject(err)
        return resolve()
      })
    })
  }

  _validationDrive () {
    return this.drive
  }
  _validateFile (name, content) {
    let drive = this._validationDrive()
    return new Promise((resolve, reject) => {
      drive.readFile(name, (err, data) => {
        if (err) return reject(err)
        if (!data.equals(content)) return reject(new Error(`Read data for ${name} does not match written content.`))
        return resolve()
      })
    })
  }
  _validateDirectory (name, list) {
    /*
    let drive = this._validationDrive()
    return new Promise((resolve, reject) => {
      drive.readdir(name, (err, list) => {
        if (err) return reject(err)
        let fileSet = new Set(list)
        for (const file of list) {
          if (!fileSet.has(file)) return reject(new Error(`Directory does not contain expected file: ${file}`))
          fileSet.delete(file)
        }
        if (fileSet.size) return reject(new Error(`Directory contains unexpected files: ${fileSet}`))
        return resolve()
      })
    })
    */
  }
  async _validate () {
    for (const [fileName, content] of this.files) {
      await this._validateFile(fileName, content)
    }
    for (const [dirName, list] of this.directories) {
      await this._validateDirectory(dirName, list)
    }
  }

  async call (ops) {
    if (!this._counter) this._counter = 0
    let res = await super.call(ops)
    this.log.push(res)
  }

  // START Fuzzing operations

  async writeFile () {
    let { name, content } = this._createFile()
    return new Promise((resolve, reject) => {
      this.drive.writeFile(name, content, err => {
        if (err) return reject(err)
        this.files.set(name, content)
        return resolve({ type: 'write', name, content })
      })
    })
  }

  async deleteFile () {
    let selected = this._selectFile()
    if (!selected) return

    let fileName = selected[0]
    return this._deleteFile(fileName)
  }

  async deleteInvalidFile () {
    let name = this._fileName()
    while (this.files.get(name)) name = this._fileName()
    try {
      await this._deleteFile(name)
    } catch (err) {
      if (err && err.code !== 'ENOENT') throw err
    }
  }

  statFile () {
    let selected = this._selectFile()
    if (!selected) return

    let [fileName, content] = selected
    return new Promise((resolve, reject) => {
      this.drive.stat(fileName, (err, st) => {
        if (err) return reject(err)
        if (!st) return reject(new Error(`File ${fileName} should exist but does not exist.`))
        if (st.size !== content.length) return reject(new Error(`Incorrect content length for file ${fileName}.`))
        return resolve({ type: 'stat', fileName, stat: st })
      })
    })
  }

  statDirectory () {
    let selected = this._selectDirectory()
    if (!selected) return

    let [dirName, { offset, byteOffset }] = selected

    this.debug(`Statting directory ${dirName}.`)
    let fileStat = JSON.stringify(this.files.get(dirName))
    this.debug(`   File stat for name: ${fileStat} and typeof ${typeof fileStat}`)
    return new Promise((resolve, reject) => {
      this.drive.stat(dirName, (err, st) => {
        if (err) return reject(err)
        this.debug(`Stat for directory ${dirName}: ${JSON.stringify(st)}`)
        if (!st) return reject(new Error(`Directory ${dirName} should exist but does not exist.`))
        if (!st.isDirectory()) return reject(new Error(`Stat for directory ${dirName} does not have directory mode`))
        if (st.offset !== offset || st.byteOffset !== byteOffset) return reject(new Error(`Invalid offsets for ${dirName}`))
        return resolve({ type: 'stat', dirName })
      })
    })
  }

  existingFileOverwrite () {
    let selected = this._selectFile()
    if (!selected) return
    let [fileName, content] = selected

    let { content: newContent } = this._createFile()

    return new Promise((resolve, reject) => {
      let writeStream = this.drive.createWriteStream(fileName)
      writeStream.on('error', err => reject(err))
      writeStream.on('finish', () => {
        this.files.set(fileName, newContent)
        resolve()
      })
      writeStream.end(newContent)
    })
  }

  randomReadStream () {
    let selected = this._selectFile()
    if (!selected) return
    let [fileName, content] = selected

    return new Promise((resolve, reject) => {
      let drive = this._validationDrive()
      let start = this.randomInt(content.length)
      this.debug(`Creating random read stream for ${fileName} at start ${start}`)
      let stream = drive.createReadStream(fileName, {
        start
      })
      collect(stream, (err, bufs) => {
        if (err) return reject(err)
        let buf = bufs.length === 1 ? bufs[0] : Buffer.concat(bufs)
        if (!buf.equals(content.slice(start))) return reject(new Error('Read stream does not match content slice.'))
        this.debug(`Random read stream for ${fileName} succeeded.`)
        return resolve()
      })
    })
  }

  writeAndMkdir () {
    const self = this

    let { name: fileName, content } = this._createFile()
    let dirName = this._fileName()

    return new Promise((resolve, reject) => {
      let pending = 2

      let offset = this.drive._contentFeedLength
      let byteOffset = this.drive._contentFeedByteLength

      let writeStream = this.drive.createWriteStream(fileName)
      writeStream.on('finish', done)

      this.drive.mkdir(dirName, done)
      writeStream.end(content)

      function done (err) {
        if (err) return reject(err)
        if (!--pending) {
          self.files.set(fileName, content)
          self.debug(`Created directory ${dirName}`)
          self.directories.set(dirName, {
            offset,
            byteOffset
          })
          return resolve()
        }
      }
    })
  }
}

class SparseHyperdriveFuzzer extends HyperdriveFuzzer {
  constructor(opts) {
    super(opts)
  }
  async _setup () {
    await super._setup()

    this.remoteDrive = create(this.drive.key)

    return new Promise((resolve, reject) => {
      this.remoteDrive.ready(err => {
        if (err) throw err
        let s1 = this.remoteDrive.replicate({ live: true })
        s1.pipe(this.drive.replicate({ live: true })).pipe(s1)
        this.remoteDrive.contentReady(err => {
          if (err) return reject(err)
          return resolve()
        })
      })
    })
  }
  _validationDrive () {
    return this.remoteDrive
  }
}

module.exports = HyperdriveFuzzer

tape('20000 mixed operations, single drive', async t => {
  t.plan(1)

  const fuzz = new HyperdriveFuzzer({
    seed: 'hyperdrive',
    debugging: false
  })

  try {
    await fuzz.run(20000)
    t.pass('fuzzing succeeded')
  } catch (err) {
    t.error(err, 'no error')
  }
})

tape('20000 mixed operations, replicating drives', async t => {
  t.plan(1)

  const fuzz = new SparseHyperdriveFuzzer({
    seed: 'hyperdrive2',
    debugging: false
  })

  try {
    await fuzz.run(20000)
    t.pass('fuzzing succeeded')
  } catch (err) {
    t.error(err, 'no error')
  }
})

tape('100 quick validations (initialization timing)', async t => {
  t.plan(1)

  try {
    for (let i = 0; i < 100; i++) {
      const fuzz = new SparseHyperdriveFuzzer({
        seed: 'iteration #' + i,
        debugging: false
      })
      await fuzz.run(100)
    }
    t.pass('fuzzing suceeded')
  } catch (err) {
    t.error(err, 'no error')
  }
})
<commit_msg>Add length to randomReadStream fuzz test<commit_after>const tape = require('tape')
const sodium = require('sodium-universal')
const collect = require('stream-collector')

const FuzzBuzz = require('fuzzbuzz')
const create = require('./helpers/create')

const MAX_PATH_DEPTH = 30
const MAX_FILE_LENGTH = 1e3
const CHARACTERS = 1e3
const INVALID_CHARS = new Set(['/', '\\', '?', '%', '*', ':', '|', '"', '<', '>', '.', ' '])

class HyperdriveFuzzer extends FuzzBuzz {
  constructor (opts) {
    super(opts)

    this.add(10, this.writeFile)
    this.add(5, this.deleteFile)
    this.add(5, this.existingFileOverwrite)
    this.add(3, this.statFile)
    this.add(3, this.statDirectory)
    this.add(2, this.deleteInvalidFile)
    this.add(2, this.randomReadStream)
    this.add(1, this.writeAndMkdir)
  }

  // START Helper functions.

  _select (map) {
    let idx = this.randomInt(map.size -1)
    if (idx < 0) return null

    let ite = map.entries()
    while (idx--) ite.next()
    return ite.next().value
  }
  _selectFile () {
    return this._select(this.files)
  }
  _selectDirectory () {
    return this._select(this.directories)
  }

  _validChar () {
    do {
      var char = String.fromCharCode(this.randomInt(CHARACTERS))
    } while (INVALID_CHARS.has(char))
    return char
  }
  _fileName () {
    do {
      let depth = Math.max(this.randomInt(MAX_PATH_DEPTH), 1)
      var name = (new Array(depth)).fill(0).map(() => this._validChar()).join('/')
    } while (this.files.get(name) || this.directories.get(name))
    return name
  }
  _createFile () {
    let name = this._fileName()
    let content = Buffer.allocUnsafe(this.randomInt(MAX_FILE_LENGTH)).fill(0).map(() => this.randomInt(10))
    return { name, content }
  }
  _deleteFile (name) {
    return new Promise((resolve, reject) => {
      this.drive.unlink(name, err => {
        if (err) return reject(err)
        this.files.delete(name)
        return resolve({ type: 'delete', name })
      })
    })
  }

  // START FuzzBuzz interface

  _setup () {
    this.drive = create()
    this.files = new Map()
    this.directories = new Map()
    this.streams = new Map()
    this.log = []

    return new Promise((resolve, reject) => {
      this.drive.contentReady(err => {
        if (err) return reject(err)
        return resolve()
      })
    })
  }

  _validationDrive () {
    return this.drive
  }
  _validateFile (name, content) {
    let drive = this._validationDrive()
    return new Promise((resolve, reject) => {
      drive.readFile(name, (err, data) => {
        if (err) return reject(err)
        if (!data.equals(content)) return reject(new Error(`Read data for ${name} does not match written content.`))
        return resolve()
      })
    })
  }
  _validateDirectory (name, list) {
    /*
    let drive = this._validationDrive()
    return new Promise((resolve, reject) => {
      drive.readdir(name, (err, list) => {
        if (err) return reject(err)
        let fileSet = new Set(list)
        for (const file of list) {
          if (!fileSet.has(file)) return reject(new Error(`Directory does not contain expected file: ${file}`))
          fileSet.delete(file)
        }
        if (fileSet.size) return reject(new Error(`Directory contains unexpected files: ${fileSet}`))
        return resolve()
      })
    })
    */
  }
  async _validate () {
    for (const [fileName, content] of this.files) {
      await this._validateFile(fileName, content)
    }
    for (const [dirName, list] of this.directories) {
      await this._validateDirectory(dirName, list)
    }
  }

  async call (ops) {
    if (!this._counter) this._counter = 0
    let res = await super.call(ops)
    this.log.push(res)
  }

  // START Fuzzing operations

  async writeFile () {
    let { name, content } = this._createFile()
    return new Promise((resolve, reject) => {
      this.drive.writeFile(name, content, err => {
        if (err) return reject(err)
        this.files.set(name, content)
        return resolve({ type: 'write', name, content })
      })
    })
  }

  async deleteFile () {
    let selected = this._selectFile()
    if (!selected) return

    let fileName = selected[0]
    return this._deleteFile(fileName)
  }

  async deleteInvalidFile () {
    let name = this._fileName()
    while (this.files.get(name)) name = this._fileName()
    try {
      await this._deleteFile(name)
    } catch (err) {
      if (err && err.code !== 'ENOENT') throw err
    }
  }

  statFile () {
    let selected = this._selectFile()
    if (!selected) return

    let [fileName, content] = selected
    return new Promise((resolve, reject) => {
      this.drive.stat(fileName, (err, st) => {
        if (err) return reject(err)
        if (!st) return reject(new Error(`File ${fileName} should exist but does not exist.`))
        if (st.size !== content.length) return reject(new Error(`Incorrect content length for file ${fileName}.`))
        return resolve({ type: 'stat', fileName, stat: st })
      })
    })
  }

  statDirectory () {
    let selected = this._selectDirectory()
    if (!selected) return

    let [dirName, { offset, byteOffset }] = selected

    this.debug(`Statting directory ${dirName}.`)
    let fileStat = JSON.stringify(this.files.get(dirName))
    this.debug(`   File stat for name: ${fileStat} and typeof ${typeof fileStat}`)
    return new Promise((resolve, reject) => {
      this.drive.stat(dirName, (err, st) => {
        if (err) return reject(err)
        this.debug(`Stat for directory ${dirName}: ${JSON.stringify(st)}`)
        if (!st) return reject(new Error(`Directory ${dirName} should exist but does not exist.`))
        if (!st.isDirectory()) return reject(new Error(`Stat for directory ${dirName} does not have directory mode`))
        if (st.offset !== offset || st.byteOffset !== byteOffset) return reject(new Error(`Invalid offsets for ${dirName}`))
        return resolve({ type: 'stat', dirName })
      })
    })
  }

  existingFileOverwrite () {
    let selected = this._selectFile()
    if (!selected) return
    let [fileName, content] = selected

    let { content: newContent } = this._createFile()

    return new Promise((resolve, reject) => {
      let writeStream = this.drive.createWriteStream(fileName)
      writeStream.on('error', err => reject(err))
      writeStream.on('finish', () => {
        this.files.set(fileName, newContent)
        resolve()
      })
      writeStream.end(newContent)
    })
  }

  randomReadStream () {
    let selected = this._selectFile()
    if (!selected) return
    let [fileName, content] = selected

    return new Promise((resolve, reject) => {
      let drive = this._validationDrive()
      let start = this.randomInt(content.length)
      let length = this.randomInt(content.length - start)
      this.debug(`Creating random read stream for ${fileName} at start ${start} with length ${length}`)
      let stream = drive.createReadStream(fileName, {
        start,
        length
      })
      collect(stream, (err, bufs) => {
        if (err) return reject(err)
        let buf = bufs.length === 1 ? bufs[0] : Buffer.concat(bufs)
        if (!buf.equals(content.slice(start, start + length))) return reject(new Error('Read stream does not match content slice.'))
        this.debug(`Random read stream for ${fileName} succeeded.`)
        return resolve()
      })
    })
  }

  writeAndMkdir () {
    const self = this

    let { name: fileName, content } = this._createFile()
    let dirName = this._fileName()

    return new Promise((resolve, reject) => {
      let pending = 2

      let offset = this.drive._contentFeedLength
      let byteOffset = this.drive._contentFeedByteLength

      let writeStream = this.drive.createWriteStream(fileName)
      writeStream.on('finish', done)

      this.drive.mkdir(dirName, done)
      writeStream.end(content)

      function done (err) {
        if (err) return reject(err)
        if (!--pending) {
          self.files.set(fileName, content)
          self.debug(`Created directory ${dirName}`)
          self.directories.set(dirName, {
            offset,
            byteOffset
          })
          return resolve()
        }
      }
    })
  }
}

class SparseHyperdriveFuzzer extends HyperdriveFuzzer {
  constructor(opts) {
    super(opts)
  }
  async _setup () {
    await super._setup()

    this.remoteDrive = create(this.drive.key)

    return new Promise((resolve, reject) => {
      this.remoteDrive.ready(err => {
        if (err) throw err
        let s1 = this.remoteDrive.replicate({ live: true })
        s1.pipe(this.drive.replicate({ live: true })).pipe(s1)
        this.remoteDrive.contentReady(err => {
          if (err) return reject(err)
          return resolve()
        })
      })
    })
  }
  _validationDrive () {
    return this.remoteDrive
  }
}

module.exports = HyperdriveFuzzer

tape('20000 mixed operations, single drive', async t => {
  t.plan(1)

  const fuzz = new HyperdriveFuzzer({
    seed: 'hyperdrive',
    debugging: false
  })

  try {
    await fuzz.run(20000)
    t.pass('fuzzing succeeded')
  } catch (err) {
    t.error(err, 'no error')
  }
})

tape('20000 mixed operations, replicating drives', async t => {
  t.plan(1)

  const fuzz = new SparseHyperdriveFuzzer({
    seed: 'hyperdrive2',
    debugging: false
  })

  try {
    await fuzz.run(20000)
    t.pass('fuzzing succeeded')
  } catch (err) {
    t.error(err, 'no error')
  }
})

tape('100 quick validations (initialization timing)', async t => {
  t.plan(1)

  try {
    for (let i = 0; i < 100; i++) {
      const fuzz = new SparseHyperdriveFuzzer({
        seed: 'iteration #' + i,
        debugging: false
      })
      await fuzz.run(100)
    }
    t.pass('fuzzing suceeded')
  } catch (err) {
    t.error(err, 'no error')
  }
})
<|endoftext|>


<commit_before>module.exports = function (el, value) {
  el.style.setProperty(this.args[0], value)
}
<commit_msg>fix: Style binder must stay silent if the style is invalid (according to platform)<commit_after>module.exports = function (el, value) {
  try {
    el.style.setProperty(this.args[0], value)
  } catch(_) { }
}
<|endoftext|>


<commit_before>require('chalk').enabled = false;

var Q = require('q');
var path = require('path');
var mkdirp = require('mkdirp');
var rimraf = require('../lib/util/rimraf');
var uuid = require('node-uuid');
var object = require('mout/object');
var fs = require('../lib/util/fs');
var glob = require('glob');
var os = require('os');
var which = require('which');
var path = require('path');
var proxyquire = require('proxyquire').noCallThru().noPreserveCache();
var spawnSync = require('spawn-sync');
var config = require('../lib/config');

// For better promise errors
Q.longStackSupport = true;

// Those are needed for Travis or not configured git environment
var env = {
    'GIT_AUTHOR_DATE': 'Sun Apr 7 22:13:13 2013 +0000',
    'GIT_AUTHOR_NAME': 'André Cruz',
    'GIT_AUTHOR_EMAIL': 'amdfcruz@gmail.com',
    'GIT_COMMITTER_DATE': 'Sun Apr 7 22:13:13 2013 +0000',
    'GIT_COMMITTER_NAME': 'André Cruz',
    'GIT_COMMITTER_EMAIL': 'amdfcruz@gmail.com',
    'NODE_ENV': 'test'
};

object.mixIn(process.env, env);

var tmpLocation = path.join(
    os.tmpdir ? os.tmpdir() : os.tmpDir(),
    'bower-tests',
    uuid.v4().slice(0, 8)
);

exports.require = function (name, stubs) {
    if (stubs) {
        return proxyquire(path.join(__dirname, '../', name), stubs);
    } else {
        return require(path.join(__dirname, '../', name));
    }
};

// We need to reset cache because tests are reusing temp directories
beforeEach(function () {
    config.reset();
});

after(function () {
    rimraf.sync(tmpLocation);
});

exports.TempDir = (function() {
    function TempDir (defaults) {
        this.path = path.join(tmpLocation, uuid.v4());
        this.defaults = defaults;
    }

    TempDir.prototype.create = function (files, defaults) {
        var that = this;

        defaults = defaults || this.defaults || {};
        files = object.merge(files || {}, defaults);

        this.meta = function(tag) {
            if (tag) {
                return files[tag]['bower.json'];
            } else {
                return files['bower.json'];
            }
        };

        if (files) {
            object.forOwn(files, function (contents, filepath) {
                if (typeof contents === 'object') {
                    contents = JSON.stringify(contents, null, ' ') + '\n';
                }

                var fullPath = path.join(that.path, filepath);
                mkdirp.sync(path.dirname(fullPath));
                fs.writeFileSync(fullPath, contents);
            });
        }

        return this;
    };

    TempDir.prototype.prepare = function (files) {
        rimraf.sync(this.path);
        mkdirp.sync(this.path);
        this.create(files);

        return this;
    };

    // TODO: Rewrite to synchronous form
    TempDir.prototype.prepareGit = function (revisions) {
        var that = this;

        revisions = object.merge(revisions || {}, this.defaults);

        rimraf.sync(that.path);

        mkdirp.sync(that.path);

        this.git('init');

        this.glob('./!(.git)').map(function (removePath) {
            var fullPath = path.join(that.path, removePath);

            rimraf.sync(fullPath);
        });

        object.forOwn(revisions, function (files, tag) {
            this.create(files, {});
            this.git('add', '-A');
            this.git('commit', '-m"commit"');
            this.git('tag', tag);
        }.bind(this));

        return this;
    };

    TempDir.prototype.glob = function (pattern) {
        return glob.sync(pattern, {
            cwd: this.path,
            dot: true
        });
    };

    TempDir.prototype.getPath = function (name) {
        return path.join(this.path, name);
    };

    TempDir.prototype.read = function (name) {
        return fs.readFileSync(this.getPath(name), 'utf8');
    };

    TempDir.prototype.readJson = function (name) {
        return JSON.parse(this.read(name));
    };

    TempDir.prototype.git = function () {
        var args = Array.prototype.slice.call(arguments);
        var result = spawnSync('git', args, { cwd: this.path });

        if (result.status !== 0) {
            throw new Error(result.stderr);
        } else {
            return result.stdout.toString();
        }
    };

    TempDir.prototype.exists = function (name) {
        return fs.existsSync(path.join(this.path, name));
    };

    return TempDir;
})();

exports.expectEvent = function expectEvent(emitter, eventName) {
    var deferred = Q.defer();

    emitter.once(eventName, function () {
        deferred.resolve(arguments);
    });

    emitter.once('error', function (reason) {
        deferred.reject(reason);
    });

    return deferred.promise;
};

exports.command = function (command, stubs) {
    var rawCommand;
    var commandStubs = {};

    stubs = stubs || {};
    var cwd = stubs.cwd;
    delete stubs.cwd;

    rawCommand = exports.require(
        'lib/commands/' + command, stubs
    );

    commandStubs['./' + command] = function () {
        var args = [].slice.call(arguments);
        args[rawCommand.length - 1] = object.merge({ cwd: cwd }, args[rawCommand.length - 1] || {});
        return rawCommand.apply(null, args);
    };

    var instance = exports.require(
        'lib/commands/index', commandStubs
    );

    var commandParts = command.split('/');

    while (commandParts.length > 0) {
        instance = instance[commandParts.shift()];
    }

    if (!instance) {
        throw new Error('Unknown command: ' + command);
    }

    // TODO: refactor tests, so they can use readOptions directly
    instance.readOptions = function (argv) {
        argv = ['node', 'bower'].concat(argv);
        argv = command.split('/').concat(argv);

        return rawCommand.readOptions(argv);
    };

    return instance;
};

exports.run = function (command, args) {
    var logger = command.apply(command, args || []);

    // Hack so we can intercept prompring for data
    logger.prompt = function(data) {
        logger.emit('confirm', data);
    };

    var promise = exports.expectEvent(logger, 'end');

    promise.logger = logger;

    return promise;
};

// Captures all stdout and stderr
exports.capture = function(callback) {
    var oldStdout = process.stdout.write;
    var oldStderr = process.stderr.write;

    var stdout = '';
    var stderr = '';

    process.stdout.write = function(text) {
        stdout += text;
    };

    process.stderr.write = function(text) {
        stderr += text;
    };

    return Q.fcall(callback).then(function() {
        process.stdout.write = oldStdout;
        process.stderr.write = oldStderr;

        return [stdout, stderr];
    }).fail(function(e) {
        process.stdout.write = oldStdout;
        process.stderr.write = oldStderr;

        throw e;
    });
};

exports.hasSvn = function() {
    try {
        which.sync('svn');
        return true;
    } catch (ex) {
        return false;
    }
};

exports.isWin = function() {
    return process.platform === 'win32';
};

exports.localSource = function (localPath) {
    localPath = path.normalize(localPath);

    if (!exports.isWin()) {
        localPath = 'file://' + localPath;
    }

    return localPath;
};

// Used for example by "svn checkout" and "svn export"
exports.localUrl = function (localPath) {
    localPath = path.normalize(localPath);

    if (!exports.isWin()) {
        localPath = 'file://' + localPath;
    } else {
        localPath = 'file:///' + localPath;
    }

    return localPath;
};
<commit_msg>sanitize generated UUIDs<commit_after>require('chalk').enabled = false;

var Q = require('q');
var path = require('path');
var mkdirp = require('mkdirp');
var rimraf = require('../lib/util/rimraf');
var uuid = require('node-uuid');
var object = require('mout/object');
var fs = require('../lib/util/fs');
var glob = require('glob');
var os = require('os');
var which = require('which');
var path = require('path');
var proxyquire = require('proxyquire').noCallThru().noPreserveCache();
var spawnSync = require('spawn-sync');
var config = require('../lib/config');

// For better promise errors
Q.longStackSupport = true;

// Those are needed for Travis or not configured git environment
var env = {
    'GIT_AUTHOR_DATE': 'Sun Apr 7 22:13:13 2013 +0000',
    'GIT_AUTHOR_NAME': 'André Cruz',
    'GIT_AUTHOR_EMAIL': 'amdfcruz@gmail.com',
    'GIT_COMMITTER_DATE': 'Sun Apr 7 22:13:13 2013 +0000',
    'GIT_COMMITTER_NAME': 'André Cruz',
    'GIT_COMMITTER_EMAIL': 'amdfcruz@gmail.com',
    'NODE_ENV': 'test'
};

object.mixIn(process.env, env);

var tmpLocation = path.join(
    os.tmpdir ? os.tmpdir() : os.tmpDir(),
    'bower-tests',
    sanitizePackageName(uuid.v4().slice(0, 8))
);

exports.require = function (name, stubs) {
    if (stubs) {
        return proxyquire(path.join(__dirname, '../', name), stubs);
    } else {
        return require(path.join(__dirname, '../', name));
    }
};

// We need to reset cache because tests are reusing temp directories
beforeEach(function () {
    config.reset();
});

after(function () {
    rimraf.sync(tmpLocation);
});

exports.TempDir = (function() {
    function TempDir (defaults) {
        this.path = path.join(tmpLocation, sanitizePackageName(uuid.v4()));
        this.defaults = defaults;
    }

    TempDir.prototype.create = function (files, defaults) {
        var that = this;

        defaults = defaults || this.defaults || {};
        files = object.merge(files || {}, defaults);

        this.meta = function(tag) {
            if (tag) {
                return files[tag]['bower.json'];
            } else {
                return files['bower.json'];
            }
        };

        if (files) {
            object.forOwn(files, function (contents, filepath) {
                if (typeof contents === 'object') {
                    contents = JSON.stringify(contents, null, ' ') + '\n';
                }

                var fullPath = path.join(that.path, filepath);
                mkdirp.sync(path.dirname(fullPath));
                fs.writeFileSync(fullPath, contents);
            });
        }

        return this;
    };

    TempDir.prototype.prepare = function (files) {
        rimraf.sync(this.path);
        mkdirp.sync(this.path);
        this.create(files);

        return this;
    };

    // TODO: Rewrite to synchronous form
    TempDir.prototype.prepareGit = function (revisions) {
        var that = this;

        revisions = object.merge(revisions || {}, this.defaults);

        rimraf.sync(that.path);

        mkdirp.sync(that.path);

        this.git('init');

        this.glob('./!(.git)').map(function (removePath) {
            var fullPath = path.join(that.path, removePath);

            rimraf.sync(fullPath);
        });

        object.forOwn(revisions, function (files, tag) {
            this.create(files, {});
            this.git('add', '-A');
            this.git('commit', '-m"commit"');
            this.git('tag', tag);
        }.bind(this));

        return this;
    };

    TempDir.prototype.glob = function (pattern) {
        return glob.sync(pattern, {
            cwd: this.path,
            dot: true
        });
    };

    TempDir.prototype.getPath = function (name) {
        return path.join(this.path, name);
    };

    TempDir.prototype.read = function (name) {
        return fs.readFileSync(this.getPath(name), 'utf8');
    };

    TempDir.prototype.readJson = function (name) {
        return JSON.parse(this.read(name));
    };

    TempDir.prototype.git = function () {
        var args = Array.prototype.slice.call(arguments);
        var result = spawnSync('git', args, { cwd: this.path });

        if (result.status !== 0) {
            throw new Error(result.stderr);
        } else {
            return result.stdout.toString();
        }
    };

    TempDir.prototype.exists = function (name) {
        return fs.existsSync(path.join(this.path, name));
    };

    return TempDir;
})();

exports.expectEvent = function expectEvent(emitter, eventName) {
    var deferred = Q.defer();

    emitter.once(eventName, function () {
        deferred.resolve(arguments);
    });

    emitter.once('error', function (reason) {
        deferred.reject(reason);
    });

    return deferred.promise;
};

exports.command = function (command, stubs) {
    var rawCommand;
    var commandStubs = {};

    stubs = stubs || {};
    var cwd = stubs.cwd;
    delete stubs.cwd;

    rawCommand = exports.require(
        'lib/commands/' + command, stubs
    );

    commandStubs['./' + command] = function () {
        var args = [].slice.call(arguments);
        args[rawCommand.length - 1] = object.merge({ cwd: cwd }, args[rawCommand.length - 1] || {});
        return rawCommand.apply(null, args);
    };

    var instance = exports.require(
        'lib/commands/index', commandStubs
    );

    var commandParts = command.split('/');

    while (commandParts.length > 0) {
        instance = instance[commandParts.shift()];
    }

    if (!instance) {
        throw new Error('Unknown command: ' + command);
    }

    // TODO: refactor tests, so they can use readOptions directly
    instance.readOptions = function (argv) {
        argv = ['node', 'bower'].concat(argv);
        argv = command.split('/').concat(argv);

        return rawCommand.readOptions(argv);
    };

    return instance;
};

exports.run = function (command, args) {
    var logger = command.apply(command, args || []);

    // Hack so we can intercept prompring for data
    logger.prompt = function(data) {
        logger.emit('confirm', data);
    };

    var promise = exports.expectEvent(logger, 'end');

    promise.logger = logger;

    return promise;
};

// Captures all stdout and stderr
exports.capture = function(callback) {
    var oldStdout = process.stdout.write;
    var oldStderr = process.stderr.write;

    var stdout = '';
    var stderr = '';

    process.stdout.write = function(text) {
        stdout += text;
    };

    process.stderr.write = function(text) {
        stderr += text;
    };

    return Q.fcall(callback).then(function() {
        process.stdout.write = oldStdout;
        process.stderr.write = oldStderr;

        return [stdout, stderr];
    }).fail(function(e) {
        process.stdout.write = oldStdout;
        process.stderr.write = oldStderr;

        throw e;
    });
};

exports.hasSvn = function() {
    try {
        which.sync('svn');
        return true;
    } catch (ex) {
        return false;
    }
};

exports.isWin = function() {
    return process.platform === 'win32';
};

exports.localSource = function (localPath) {
    localPath = path.normalize(localPath);

    if (!exports.isWin()) {
        localPath = 'file://' + localPath;
    }

    return localPath;
};

// Used for example by "svn checkout" and "svn export"
exports.localUrl = function (localPath) {
    localPath = path.normalize(localPath);

    if (!exports.isWin()) {
        localPath = 'file://' + localPath;
    } else {
        localPath = 'file:///' + localPath;
    }

    return localPath;
};

function sanitizePackageName(unsanitizedName) {
    // enforce uuid begin with a lowercase character
    return 'a' + unsanitizedName.toLowerCase().slice(1,unsanitizedName.length);
}
<|endoftext|>


<commit_before>var anyDB = require('../')
var test = require('tap').test
require('sqlite3').verbose()

databaseUrls = {
	mysql: "mysql://root@localhost/any_db_test",
	postgres: "postgres://postgres@localhost/any_db_test",
	sqlite3: "sqlite3://:memory:",
}

/**
 * Run ``callback(conn, tap_test)`` where ``conn`` is a connection to the test
 * database, and ``tap_test`` is a node-tap test object
 */
exports.allDrivers = testRunner(function (description, opts, callback) {
	_testEachDriver(description, function (connString, t) {
		anyDB.createConnection(connString, function (err, conn) {
			if (err) throw err
			if (opts.autoEnd !== false) t.on('end', conn.end.bind(conn))
			callback(conn, t)
		})
	})
})

/**
 * Run ``callback(tx, tap_test)`` where ``tx`` is an open transaction
 * on the test database, and ``tap_test`` is a node-tap test object
 */
exports.allTransactions = testRunner(function (description, opts, callback) {
	_testEachDriver(description, function (connString, t) {
		anyDB.createConnection(connString, function (err, conn) {
			if (err) throw err
			var tx = conn.begin()
			tx.proxyEvent('error', conn)
			t.on('end', tx.rollback.bind(tx))
			t.on('end', conn.end.bind(conn))
			callback(tx, t)
		})
	})
})

/**
 * Run ``callback(pool, tap_test)`` where ``pool`` is a connection pool
 * that will connect to the test database and ``tap_test`` is a node-tap test
 * object.
 */
exports.allPools = testRunner(function (description, opts, callback) {
	var dbname = 'db_any_test'
		, i = 0;
	_testEachDriver(description, function (connString, t) {
		var pool = anyDB.createPool(connString, {
			max: 2,
			min: 0,
			idleTimoutMillis: 1000
		})
		t.on('end', pool.close.bind(pool))
		callback(pool, t)
	})
})

function _testEachDriver (description, callback) {
	Object.keys(databaseUrls).forEach(function (driverName) {
		test(description + ' - ' + driverName, function (t) {
			callback(databaseUrls[driverName], t)
		})
	})
}

function testRunner (run) {
	return function (description, opts, callback) {
		if (!callback) {
			callback = opts
			opts = {}
		}
		run(description, opts, callback)
	}
}
<commit_msg>Fix ordering of cleanup actions in transaction test helper<commit_after>var anyDB = require('../')
var test = require('tap').test
require('sqlite3').verbose()

databaseUrls = {
	sqlite3: "sqlite3://:memory:",
	mysql: "mysql://root@localhost/any_db_test",
	postgres: "postgres://postgres@localhost/any_db_test",
}

/**
 * Run ``callback(conn, tap_test)`` where ``conn`` is a connection to the test
 * database, and ``tap_test`` is a node-tap test object
 */
exports.allDrivers = testRunner(function (description, opts, callback) {
	_testEachDriver(description, function (connString, t) {
		anyDB.createConnection(connString, function (err, conn) {
			if (err) throw err
			if (opts.autoEnd !== false) t.on('end', conn.end.bind(conn))
			callback(conn, t)
		})
	})
})

/**
 * Run ``callback(tx, tap_test)`` where ``tx`` is an open transaction
 * on the test database, and ``tap_test`` is a node-tap test object
 */
exports.allTransactions = testRunner(function (description, opts, callback) {
	_testEachDriver(description, function (connString, t) {
		anyDB.createConnection(connString, function (err, conn) {
			if (err) throw err
			var tx = conn.begin()
			tx.driver = connString.split(':').shift()
			t.on('end', tx.rollback.bind(tx, conn.end.bind(conn)))
			tx.on('error', function (err) { debugger; t.emit('error', err) })
			callback(tx, t)
		})
	})
})

/**
 * Run ``callback(pool, tap_test)`` where ``pool`` is a connection pool
 * that will connect to the test database and ``tap_test`` is a node-tap test
 * object.
 */
exports.allPools = testRunner(function (description, opts, callback) {
	var dbname = 'db_any_test'
		, i = 0;
	_testEachDriver(description, function (connString, t) {
		var pool = anyDB.createPool(connString, {
			max: 2,
			min: 0,
			idleTimoutMillis: 1000
		})
		t.on('end', pool.close.bind(pool))
		callback(pool, t)
	})
})

function _testEachDriver (description, callback) {
	Object.keys(databaseUrls).forEach(function (driverName) {
		test(description + ' - ' + driverName, function (t) {
			callback(databaseUrls[driverName], t)
		})
	})
}

function testRunner (run) {
	return function (description, opts, callback) {
		if (!callback) {
			callback = opts
			opts = {}
		}
		run(description, opts, callback)
	}
}
<|endoftext|>


<commit_before>import katex from "../katex";
import ParseError from "../src/ParseError";
import parseTree from "../src/parseTree";
import Settings from "../src/Settings";

import diff from 'jest-diff';
import {RECEIVED_COLOR, printReceived, printExpected} from 'jest-matcher-utils';
import {formatStackTrace, separateMessageFromStack} from 'jest-message-util';

export function ConsoleWarning(message) {
    Error.captureStackTrace(this, global.console.warn);
    this.name = this.constructor.name;
    this.message = message;
}
Object.setPrototypeOf(ConsoleWarning.prototype, Error.prototype);

/**
 * Return the first raw string if x is tagged literal. Otherwise return x.
 */
export const r = x => x != null && x.hasOwnProperty('raw') ? x.raw[0] : x;

/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * This function is from https://github.com/facebook/jest/blob/9867e16e518d50c79
 * 492f7f0d2bc1ef8dff37db4/packages/expect/src/to_throw_matchers.js and licensed
 * under the MIT license found in the https://github.com/facebook/jest/blob/master/LICENSE.
 */
const printActualErrorMessage = error => {
    if (error) {
        const {message, stack} = separateMessageFromStack(error.stack);
        return (
            'Instead, it threw:\n' +
            /* eslint-disable-next-line new-cap */
            RECEIVED_COLOR(
                `  ${message}` +
                formatStackTrace(
                    // remove KaTeX internal stack entries
                    stack.split('\n')
                        .filter(line => line.indexOf('new ParseError') === -1)
                        .join('\n'),
                    {
                        rootDir: process.cwd(),
                        testMatch: [],
                    },
                    {
                        noStackTrace: false,
                    },
                ),
            )
        );
    }
    return 'But it didn\'t throw anything.';
};

const printExpectedResult = (mode, isNot, expectedError) => expectedError == null
    ? (isNot ? 'fail ' : 'success ') + mode
    : (isNot ? 'not throw a ' : `fail ${mode} with a `) +
        (expectedError.name || `ParseError matching "${expectedError}"`);

export const nonstrictSettings = new Settings({strict: false});
export const strictSettings = new Settings({strict: true});
export const trustSettings = new Settings({trust: true});

/**
 * Return the root node of the rendered HTML.
 * @param expr
 * @param settings
 * @returns {Object}
 */
export function getBuilt(expr, settings = new Settings()) {
    expr = r(expr); // support tagging literals
    let rootNode = katex.__renderToDomTree(expr, settings);

    if (rootNode.classes.indexOf('katex-error') >= 0) {
        return rootNode;
    }

    if (rootNode.classes.indexOf('katex-display') >= 0) {
        rootNode = rootNode.children[0];
    }

    // grab the root node of the HTML rendering
    // rootNode.children[0] is the MathML rendering
    const builtHTML = rootNode.children[1];

    // combine the non-strut children of all base spans
    const children = [];
    for (let i = 0; i < builtHTML.children.length; i++) {
        children.push(...builtHTML.children[i].children.filter(
            (node) => node.classes.indexOf("strut") < 0));
    }
    return children;
}

/**
 * Return the root node of the parse tree.
 * @param expr
 * @param settings
 * @returns {Object}
 */
export function getParsed(expr, settings = new Settings()) {
    expr = r(expr); // support tagging literals
    return parseTree(expr, settings);
}

export const stripPositions = expr => {
    if (typeof expr !== "object" || expr === null) {
        return expr;
    }
    if (expr.loc && expr.loc.lexer && typeof expr.loc.start === "number") {
        delete expr.loc;
    }
    Object.keys(expr).forEach(function(key) {
        stripPositions(expr[key]);
    });
    return expr;
};

export const Mode = {
    PARSE: {
        apply: getParsed,
        noun: 'parsing',
        Verb: 'Parse',
    },
    BUILD: {
        apply: getBuilt,
        noun: 'building',
        Verb: 'Build',
    },
};

export const expectKaTeX = (expr, settings, mode, isNot, expectedError) => {
    let pass = expectedError == null;
    let error;
    try {
        mode.apply(expr, settings);
    } catch (e) {
        error = e;
        if (e instanceof ParseError) {
            pass = expectedError === ParseError || (typeof expectedError ===
                "string" && e.message === `KaTeX parse error: ${expectedError}`);
        } else if (e instanceof ConsoleWarning) {
            pass = expectedError === ConsoleWarning;
        } else {
            pass = !!isNot; // always fail
        }
    }
    return {
        pass,
        message: () => 'Expected the expression to ' +
            printExpectedResult(mode.noun, isNot, expectedError) +
            `:\n  ${printReceived(r(expr))}\n` +
            printActualErrorMessage(error),
    };
};

export const expectEquivalent = (actual, expected, settings, mode, expand) => {
    const actualTree = stripPositions(mode.apply(actual, settings));
    const expectedTree = stripPositions(mode.apply(expected, settings));
    const pass = JSON.stringify(actualTree) === JSON.stringify(expectedTree);

    return {
        pass,
        message: pass
            ? () =>
                `${mode.Verb} trees of ${printReceived(r(actual))} and ` +
                `${printExpected(r(expected))} are equivalent`
            : () => {
                const diffString = diff(expectedTree, actualTree, {
                    expand,
                });

                return `${mode.Verb} trees of ${printReceived(r(actual))} and ` +
                `${printExpected(r(expected))} are not equivalent` +
                (diffString ? `:\n\n${diffString}` : '');
            },
    };
};
<commit_msg>chore(tests): fix use of jest-diff (#3215)<commit_after>import katex from "../katex";
import ParseError from "../src/ParseError";
import parseTree from "../src/parseTree";
import Settings from "../src/Settings";

import {diff} from 'jest-diff';
import {RECEIVED_COLOR, printReceived, printExpected} from 'jest-matcher-utils';
import {formatStackTrace, separateMessageFromStack} from 'jest-message-util';

export function ConsoleWarning(message) {
    Error.captureStackTrace(this, global.console.warn);
    this.name = this.constructor.name;
    this.message = message;
}
Object.setPrototypeOf(ConsoleWarning.prototype, Error.prototype);

/**
 * Return the first raw string if x is tagged literal. Otherwise return x.
 */
export const r = x => x != null && x.hasOwnProperty('raw') ? x.raw[0] : x;

/**
 * Copyright (c) 2014-present, Facebook, Inc. All rights reserved.
 *
 * This function is from https://github.com/facebook/jest/blob/9867e16e518d50c79
 * 492f7f0d2bc1ef8dff37db4/packages/expect/src/to_throw_matchers.js and licensed
 * under the MIT license found in the https://github.com/facebook/jest/blob/master/LICENSE.
 */
const printActualErrorMessage = error => {
    if (error) {
        const {message, stack} = separateMessageFromStack(error.stack);
        return (
            'Instead, it threw:\n' +
            /* eslint-disable-next-line new-cap */
            RECEIVED_COLOR(
                `  ${message}` +
                formatStackTrace(
                    // remove KaTeX internal stack entries
                    stack.split('\n')
                        .filter(line => line.indexOf('new ParseError') === -1)
                        .join('\n'),
                    {
                        rootDir: process.cwd(),
                        testMatch: [],
                    },
                    {
                        noStackTrace: false,
                    },
                ),
            )
        );
    }
    return 'But it didn\'t throw anything.';
};

const printExpectedResult = (mode, isNot, expectedError) => expectedError == null
    ? (isNot ? 'fail ' : 'success ') + mode
    : (isNot ? 'not throw a ' : `fail ${mode} with a `) +
        (expectedError.name || `ParseError matching "${expectedError}"`);

export const nonstrictSettings = new Settings({strict: false});
export const strictSettings = new Settings({strict: true});
export const trustSettings = new Settings({trust: true});

/**
 * Return the root node of the rendered HTML.
 * @param expr
 * @param settings
 * @returns {Object}
 */
export function getBuilt(expr, settings = new Settings()) {
    expr = r(expr); // support tagging literals
    let rootNode = katex.__renderToDomTree(expr, settings);

    if (rootNode.classes.indexOf('katex-error') >= 0) {
        return rootNode;
    }

    if (rootNode.classes.indexOf('katex-display') >= 0) {
        rootNode = rootNode.children[0];
    }

    // grab the root node of the HTML rendering
    // rootNode.children[0] is the MathML rendering
    const builtHTML = rootNode.children[1];

    // combine the non-strut children of all base spans
    const children = [];
    for (let i = 0; i < builtHTML.children.length; i++) {
        children.push(...builtHTML.children[i].children.filter(
            (node) => node.classes.indexOf("strut") < 0));
    }
    return children;
}

/**
 * Return the root node of the parse tree.
 * @param expr
 * @param settings
 * @returns {Object}
 */
export function getParsed(expr, settings = new Settings()) {
    expr = r(expr); // support tagging literals
    return parseTree(expr, settings);
}

export const stripPositions = expr => {
    if (typeof expr !== "object" || expr === null) {
        return expr;
    }
    if (expr.loc && expr.loc.lexer && typeof expr.loc.start === "number") {
        delete expr.loc;
    }
    Object.keys(expr).forEach(function(key) {
        stripPositions(expr[key]);
    });
    return expr;
};

export const Mode = {
    PARSE: {
        apply: getParsed,
        noun: 'parsing',
        Verb: 'Parse',
    },
    BUILD: {
        apply: getBuilt,
        noun: 'building',
        Verb: 'Build',
    },
};

export const expectKaTeX = (expr, settings, mode, isNot, expectedError) => {
    let pass = expectedError == null;
    let error;
    try {
        mode.apply(expr, settings);
    } catch (e) {
        error = e;
        if (e instanceof ParseError) {
            pass = expectedError === ParseError || (typeof expectedError ===
                "string" && e.message === `KaTeX parse error: ${expectedError}`);
        } else if (e instanceof ConsoleWarning) {
            pass = expectedError === ConsoleWarning;
        } else {
            pass = !!isNot; // always fail
        }
    }
    return {
        pass,
        message: () => 'Expected the expression to ' +
            printExpectedResult(mode.noun, isNot, expectedError) +
            `:\n  ${printReceived(r(expr))}\n` +
            printActualErrorMessage(error),
    };
};

export const expectEquivalent = (actual, expected, settings, mode, expand) => {
    const actualTree = stripPositions(mode.apply(actual, settings));
    const expectedTree = stripPositions(mode.apply(expected, settings));
    const pass = JSON.stringify(actualTree) === JSON.stringify(expectedTree);

    return {
        pass,
        message: pass
            ? () =>
                `${mode.Verb} trees of ${printReceived(r(actual))} and ` +
                `${printExpected(r(expected))} are equivalent`
            : () => {
                const diffString = diff(expectedTree, actualTree, {
                    expand,
                });

                return `${mode.Verb} trees of ${printReceived(r(actual))} and ` +
                `${printExpected(r(expected))} are not equivalent` +
                (diffString ? `:\n\n${diffString}` : '');
            },
    };
};
<|endoftext|>


<commit_before>(function(){
	var $ = jQuery;
	if( location.hash !== "" ){
		throw "hash must be clear to start the tests";
	}

	function debugEqual(a, b){
		debugger;
		equal(a,b);
	}

	// force the hash to test init and to ensure that it's the initial hash value
	location.hash = "#dialog-dialog";

	var $doc, instance, $instance, $nested, $nohist, commonSetup, commonTeardown;

	commonSetup = function() {
		$instance = $( "#dialog" );
		$nested = $( "#nested" );
		$nohist = $( "#nohist" );

		if( $instance.data("dialog") ) {
			$instance.data( "dialog" ).destroy();
		}

		if( $nested.data("dialog") ) {
			$nested.data( "dialog" ).destroy();
		}

		if( $nohist.data("dialog") ) {
			$nohist.data( "dialog" ).destroy();
		}

		$instance.dialog();
		$nested.dialog();
		$nohist.dialog();
	};

	commonTeardown = function() {
		$instance.unbind( "dialog-closed" );
		$instance.unbind( "dialog-opened" );
		$nested.unbind( "dialog-closed" );
		$nested.unbind( "dialog-opened" );
		$nohist.unbind( "dialog-closed" );
		$nohist.unbind( "dialog-opened" );

		$instance.data( "dialog" ).destroy();
		$nested.data( "dialog" ).destroy();
		$nohist.data( "dialog" ).destroy();
	};

	// we have to give the browser the time to trigger a hashchange
	// so there's no mixup
	function closeInstance($dialog){
		$dialog.trigger( "dialog-close" );
		setTimeout(function(){
			start()
		}, 400);
	}

	var initOpened;

	$(window).one("dialog-opened", function(event){
		$instance = $(event.target).data( "dialog" ).$el;
		initOpened = event.target;
	});

	asyncTest("should open on init if hash is correct", function(){
		ok(initOpened);
		closeInstance($instance);
	});

	// NOTE this must come after the first test so that the init of the dialog
	// comes from the page's enhance event
	module( "init", {
		setup: commonSetup,
		teardown: commonTeardown
	});

	asyncTest("should go back to dialogback when closing the dialog ", function(){
		expect(3);
		$instance.trigger( "dialog-open" );
		equal(location.hash, "#dialog-dialog");

		$(window).one( "hashchange", function(){
			equal(location.hash, "");

			$(window).one("hashchange", function(){

				// TODO it's not clear why the hash value isn't reflecting the change
				//			 immediately
				setTimeout(function(){
					equal(location.hash, "#dialog-dialog");
					closeInstance($instance);
				}, 1000);
			});

			window.history.back();
		});

		$instance.trigger( "dialog-close" );
	});

	// TODO move to `dialog.js` tests
	test("should prevent double init", function(){
		// the `isOpen` propery of the dialog object is set
		// to `false` at the end of init, we check that it never gets there
		$instance.data("dialog").hash = "foo";
		$instance.dialog();
		equal($instance.data("dialog").hash, "foo");
	});

	asyncTest("should append dialog name to hash for nested dialogs", function(){
		expect(3);

		$instance.trigger( "dialog-open" );
		equal(location.hash, "#dialog-dialog");
		$instance.find( "#nested-dialog-anchor" ).trigger( "click" );
		equal(location.hash, "#dialog-dialog#nested-dialog");

		$nested.one("dialog-closed", function(){
			equal("#dialog-dialog", location.hash);
			closeInstance($instance);
		});

		 window.history.back();
	});

	asyncTest("nohist dialog should still work as normal after page load", function(){
		expect(2);

		var oldHash = location.hash;

		$nohist.trigger( "dialog-open" );
		equal(location.hash, oldHash + "#nohist-dialog");
		$nohist.one("dialog-closed", function(){
			equal(oldHash, location.hash);
			closeInstance($nohist);
		});

		window.history.back();
	});
})();
<commit_msg>account for uniq id for nohistory dialog<commit_after>(function(){
	var $ = jQuery;
	if( location.hash !== "" ){
		throw "hash must be clear to start the tests";
	}

	function debugEqual(a, b){
		debugger;
		equal(a,b);
	}

	// force the hash to test init and to ensure that it's the initial hash value
	location.hash = "#dialog-dialog";

	var $doc, instance, $instance, $nested, $nohist, commonSetup, commonTeardown;

	commonSetup = function() {
		$instance = $( "#dialog" );
		$nested = $( "#nested" );
		$nohist = $( "#nohist" );

		if( $instance.data("dialog") ) {
			$instance.data( "dialog" ).destroy();
		}

		if( $nested.data("dialog") ) {
			$nested.data( "dialog" ).destroy();
		}

		if( $nohist.data("dialog") ) {
			$nohist.data( "dialog" ).destroy();
		}

		$instance.dialog();
		$nested.dialog();
		$nohist.dialog();
	};

	commonTeardown = function() {
		$instance.unbind( "dialog-closed" );
		$instance.unbind( "dialog-opened" );
		$nested.unbind( "dialog-closed" );
		$nested.unbind( "dialog-opened" );
		$nohist.unbind( "dialog-closed" );
		$nohist.unbind( "dialog-opened" );

		$instance.data( "dialog" ).destroy();
		$nested.data( "dialog" ).destroy();
		$nohist.data( "dialog" ).destroy();
	};

	// we have to give the browser the time to trigger a hashchange
	// so there's no mixup
	function closeInstance($dialog){
		$dialog.trigger( "dialog-close" );
		setTimeout(function(){
			start()
		}, 400);
	}

	var initOpened;

	$(window).one("dialog-opened", function(event){
		$instance = $(event.target).data( "dialog" ).$el;
		initOpened = event.target;
	});

	asyncTest("should open on init if hash is correct", function(){
		ok(initOpened);
		closeInstance($instance);
	});

	// NOTE this must come after the first test so that the init of the dialog
	// comes from the page's enhance event
	module( "init", {
		setup: commonSetup,
		teardown: commonTeardown
	});

	asyncTest("should go back to dialogback when closing the dialog ", function(){
		expect(3);
		$instance.trigger( "dialog-open" );
		equal(location.hash, "#dialog-dialog");

		$(window).one( "hashchange", function(){
			equal(location.hash, "");

			$(window).one("hashchange", function(){

				// TODO it's not clear why the hash value isn't reflecting the change
				//			 immediately
				setTimeout(function(){
					equal(location.hash, "#dialog-dialog");
					closeInstance($instance);
				}, 1000);
			});

			window.history.back();
		});

		$instance.trigger( "dialog-close" );
	});

	// TODO move to `dialog.js` tests
	test("should prevent double init", function(){
		// the `isOpen` propery of the dialog object is set
		// to `false` at the end of init, we check that it never gets there
		$instance.data("dialog").hash = "foo";
		$instance.dialog();
		equal($instance.data("dialog").hash, "foo");
	});

	asyncTest("should append dialog name to hash for nested dialogs", function(){
		expect(3);

		$instance.trigger( "dialog-open" );
		equal(location.hash, "#dialog-dialog");
		$instance.find( "#nested-dialog-anchor" ).trigger( "click" );
		equal(location.hash, "#dialog-dialog#nested-dialog");

		$nested.one("dialog-closed", function(){
			equal("#dialog-dialog", location.hash);
			closeInstance($instance);
		});

		 window.history.back();
	});

	asyncTest("nohist dialog should still work as normal after page load", function(){
		expect(2);

		var oldHash = location.hash;

		$nohist.trigger( "dialog-open" );
		equal(location.hash.indexOf(oldHash + "#nohist-dialog"), 0);
		$nohist.one("dialog-closed", function(){
			equal(oldHash, location.hash);
			closeInstance($nohist);
		});

		window.history.back();
	});
})();
<|endoftext|>


<commit_before>'use strict';

import React, { PureComponent } from 'react';

import { Animated, Easing, StyleSheet, ViewPropTypes, WebView } from 'react-native';

import PropTypes from 'prop-types';

import {
  isEqual,
  setState,
  getWidth,
  isSizeChanged,
  handleSizeUpdated,
  domMutationObserveScript,
  getCurrentSize,
  getRenderSize
} from './common.js';

import momoize from './momoize';

export default class AutoHeightWebView extends PureComponent {
  static propTypes = {
    hasIframe: PropTypes.bool,
    onNavigationStateChange: PropTypes.func,
    onMessage: PropTypes.func,
    source: WebView.propTypes.source,
    customScript: PropTypes.string,
    customStyle: PropTypes.string,
    enableAnimation: PropTypes.bool,
    style: ViewPropTypes.style,
    scrollEnabled: PropTypes.bool,
    // either height or width updated will trigger this
    onSizeUpdated: PropTypes.func,
    // if set to true may cause some layout issues (smaller font size)
    scalesPageToFit: PropTypes.bool,
    // only works on enable animation
    animationDuration: PropTypes.number,
    // offset of rn webview margin
    heightOffset: PropTypes.number,
    //  rn WebView callback
    onError: PropTypes.func,
    onLoad: PropTypes.func,
    onLoadStart: PropTypes.func,
    onLoadEnd: PropTypes.func,
    onShouldStartLoadWithRequest: PropTypes.func,
    // add web/files... to project root
    files: PropTypes.arrayOf(
      PropTypes.shape({
        href: PropTypes.string,
        type: PropTypes.string,
        rel: PropTypes.string
      })
    )
  };

  static defaultProps = {
    scalesPageToFit: false,
    enableAnimation: true,
    animationDuration: 555,
    heightOffset: 12
  };

  constructor(props) {
    super(props);
    const { enableAnimation, style } = props;
    enableAnimation && (this.opacityAnimatedValue = new Animated.Value(0));
    this.webView = React.createRef();
    this.state = {
      isSizeChanged: false,
      width: getWidth(style),
      height: style && style.height ? style.height : 0
    };
  }

  getUpdatedState = momoize(setState, isEqual);

  static getDerivedStateFromProps(props, state) {
    const { height: oldHeight, width: oldWidth } = state;
    const height = props.style ? props.style.height : null;
    const width = props.style ? props.style.width : null;
    if (isSizeChanged(height, oldHeight, width, oldWidth)) {
      return {
        height,
        width,
        isSizeChanged: true
      };
    }
    return null;
  }

  componentDidUpdate() {
    const { height, width, isSizeChanged } = this.state;
    if (isSizeChanged) {
      const { enableAnimation, animationDuration, onSizeUpdated } = this.props;
      if (enableAnimation) {
        Animated.timing(this.opacityAnimatedValue, {
          toValue: 1,
          duration: animationDuration
        }).start(() => {
          handleSizeUpdated(height, width, onSizeUpdated);
        });
      } else {
        handleSizeUpdated(height, width, onSizeUpdated);
      }
      this.setState({ isSizeChanged: false });
    }
  }

  handleNavigationStateChange = navState => {
    const { title } = navState;
    const { onNavigationStateChange } = this.props;
    if (!title) {
      onNavigationStateChange && onNavigationStateChange();
      return;
    }
    const [heightValue, widthValue] = title.split(',');
    const width = Number(widthValue);
    const height = Number(heightValue);
    const { height: oldHeight, width: oldWidth } = this.state;
    if (isSizeChanged(height, oldHeight, width, oldWidth)) {
      this.props.enableAnimation && this.opacityAnimatedValue.setValue(0);
      this.setState({
        isSizeChanged: true,
        height,
        width
      });
    }
    onNavigationStateChange && onNavigationStateChange();
  };

  stopLoading() {
    this.webView.stopLoading();
  }

  render() {
    const { height, width } = this.state;
    const {
      onMessage,
      onError,
      onLoad,
      onLoadStart,
      onLoadEnd,
      onShouldStartLoadWithRequest,
      scalesPageToFit,
      enableAnimation,
      heightOffset,
      style,
      scrollEnabled
    } = this.props;
    const { source, script } = this.getUpdatedState(this.props, 'web/', getBaseScript, getIframeBaseScript);
    return (
      <Animated.View
        style={[
          styles.container,
          {
            opacity: enableAnimation ? this.opacityAnimatedValue : 1,
            width,
            height: height + heightOffset
          },
          style
        ]}
      >
        <WebView
          originWhitelist={['*']}
          ref={this.webView}
          onMessage={onMessage}
          onError={onError}
          onLoad={onLoad}
          onLoadStart={onLoadStart}
          onLoadEnd={onLoadEnd}
          onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
          style={styles.webView}
          scrollEnabled={!!scrollEnabled}
          scalesPageToFit={scalesPageToFit}
          injectedJavaScript={script}
          source={source}
          onNavigationStateChange={this.handleNavigationStateChange}
        />
      </Animated.View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: 'transparent'
  },
  webView: {
    flex: 1,
    backgroundColor: 'transparent'
  }
});

const commonScript = `
    updateSize();
    window.addEventListener('load', updateSize);
    window.addEventListener('resize', updateSize);
    `;

function getBaseScript(style) {
  return `
    ;
    ${getCurrentSize}
    (function () {
        var height = 0;
        var width = ${getWidth(style)};
        var wrapper = document.createElement('div');
        wrapper.id = 'rnahw-wrapper';
        while (document.body.firstChild instanceof Node) {
            wrapper.appendChild(document.body.firstChild);
        }
        document.body.appendChild(wrapper);
        function updateSize() {
            if(document.body.offsetHeight !== height || document.body.offsetWidth !== width) {
                var size = getSize(wrapper);
                height = size.height;
                width = size.width;
                document.title = height.toString() + ',' + width.toString();
            }
        }
        ${commonScript}
        ${domMutationObserveScript}
    } ());
    `;
}

function getIframeBaseScript(style) {
  return `
    ;
    ${getCurrentSize}
    (function () {
        var height = 0;
        var width = ${getWidth(style)};
        function updateSize() {
            if(document.body.offsetHeight !== height || document.body.offsetWidth !== width) {
                var size = getSize(document.body.firstChild);
                height = size.height;
                width = size.width;
                document.title = height.toString() + ',' + width.toString();
            }
        }
        ${commonScript}
        ${domMutationObserveScript}
    } ());
    `;
}
<commit_msg>modified default animationDuration for ios.index<commit_after>'use strict';

import React, { PureComponent } from 'react';

import { Animated, Easing, StyleSheet, ViewPropTypes, WebView } from 'react-native';

import PropTypes from 'prop-types';

import {
  isEqual,
  setState,
  getWidth,
  isSizeChanged,
  handleSizeUpdated,
  domMutationObserveScript,
  getCurrentSize,
  getRenderSize
} from './common.js';

import momoize from './momoize';

export default class AutoHeightWebView extends PureComponent {
  static propTypes = {
    hasIframe: PropTypes.bool,
    onNavigationStateChange: PropTypes.func,
    onMessage: PropTypes.func,
    source: WebView.propTypes.source,
    customScript: PropTypes.string,
    customStyle: PropTypes.string,
    enableAnimation: PropTypes.bool,
    style: ViewPropTypes.style,
    scrollEnabled: PropTypes.bool,
    // either height or width updated will trigger this
    onSizeUpdated: PropTypes.func,
    // if set to true may cause some layout issues (smaller font size)
    scalesPageToFit: PropTypes.bool,
    // only works on enable animation
    animationDuration: PropTypes.number,
    // offset of rn webview margin
    heightOffset: PropTypes.number,
    //  rn WebView callback
    onError: PropTypes.func,
    onLoad: PropTypes.func,
    onLoadStart: PropTypes.func,
    onLoadEnd: PropTypes.func,
    onShouldStartLoadWithRequest: PropTypes.func,
    // add web/files... to project root
    files: PropTypes.arrayOf(
      PropTypes.shape({
        href: PropTypes.string,
        type: PropTypes.string,
        rel: PropTypes.string
      })
    )
  };

  static defaultProps = {
    scalesPageToFit: false,
    enableAnimation: true,
    animationDuration: 255,
    heightOffset: 12
  };

  constructor(props) {
    super(props);
    const { enableAnimation, style } = props;
    enableAnimation && (this.opacityAnimatedValue = new Animated.Value(0));
    this.webView = React.createRef();
    this.state = {
      isSizeChanged: false,
      width: getWidth(style),
      height: style && style.height ? style.height : 0
    };
  }

  getUpdatedState = momoize(setState, isEqual);

  static getDerivedStateFromProps(props, state) {
    const { height: oldHeight, width: oldWidth } = state;
    const height = props.style ? props.style.height : null;
    const width = props.style ? props.style.width : null;
    if (isSizeChanged(height, oldHeight, width, oldWidth)) {
      return {
        height,
        width,
        isSizeChanged: true
      };
    }
    return null;
  }

  componentDidUpdate() {
    const { height, width, isSizeChanged } = this.state;
    if (isSizeChanged) {
      const { enableAnimation, animationDuration, onSizeUpdated } = this.props;
      if (enableAnimation) {
        Animated.timing(this.opacityAnimatedValue, {
          toValue: 1,
          duration: animationDuration
        }).start(() => {
          handleSizeUpdated(height, width, onSizeUpdated);
        });
      } else {
        handleSizeUpdated(height, width, onSizeUpdated);
      }
      this.setState({ isSizeChanged: false });
    }
  }

  handleNavigationStateChange = navState => {
    const { title } = navState;
    const { onNavigationStateChange } = this.props;
    if (!title) {
      onNavigationStateChange && onNavigationStateChange();
      return;
    }
    const [heightValue, widthValue] = title.split(',');
    const width = Number(widthValue);
    const height = Number(heightValue);
    const { height: oldHeight, width: oldWidth } = this.state;
    if (isSizeChanged(height, oldHeight, width, oldWidth)) {
      this.props.enableAnimation && this.opacityAnimatedValue.setValue(0);
      this.setState({
        isSizeChanged: true,
        height,
        width
      });
    }
    onNavigationStateChange && onNavigationStateChange();
  };

  stopLoading() {
    this.webView.stopLoading();
  }

  render() {
    const { height, width } = this.state;
    const {
      onMessage,
      onError,
      onLoad,
      onLoadStart,
      onLoadEnd,
      onShouldStartLoadWithRequest,
      scalesPageToFit,
      enableAnimation,
      heightOffset,
      style,
      scrollEnabled
    } = this.props;
    const { source, script } = this.getUpdatedState(this.props, 'web/', getBaseScript, getIframeBaseScript);
    return (
      <Animated.View
        style={[
          styles.container,
          {
            opacity: enableAnimation ? this.opacityAnimatedValue : 1,
            width,
            height: height + heightOffset
          },
          style
        ]}
      >
        <WebView
          originWhitelist={['*']}
          ref={this.webView}
          onMessage={onMessage}
          onError={onError}
          onLoad={onLoad}
          onLoadStart={onLoadStart}
          onLoadEnd={onLoadEnd}
          onShouldStartLoadWithRequest={onShouldStartLoadWithRequest}
          style={styles.webView}
          scrollEnabled={!!scrollEnabled}
          scalesPageToFit={scalesPageToFit}
          injectedJavaScript={script}
          source={source}
          onNavigationStateChange={this.handleNavigationStateChange}
        />
      </Animated.View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    backgroundColor: 'transparent'
  },
  webView: {
    flex: 1,
    backgroundColor: 'transparent'
  }
});

const commonScript = `
    updateSize();
    window.addEventListener('load', updateSize);
    window.addEventListener('resize', updateSize);
    `;

function getBaseScript(style) {
  return `
    ;
    ${getCurrentSize}
    (function () {
        var height = 0;
        var width = ${getWidth(style)};
        var wrapper = document.createElement('div');
        wrapper.id = 'rnahw-wrapper';
        while (document.body.firstChild instanceof Node) {
            wrapper.appendChild(document.body.firstChild);
        }
        document.body.appendChild(wrapper);
        function updateSize() {
            if(document.body.offsetHeight !== height || document.body.offsetWidth !== width) {
                var size = getSize(wrapper);
                height = size.height;
                width = size.width;
                document.title = height.toString() + ',' + width.toString();
            }
        }
        ${commonScript}
        ${domMutationObserveScript}
    } ());
    `;
}

function getIframeBaseScript(style) {
  return `
    ;
    ${getCurrentSize}
    (function () {
        var height = 0;
        var width = ${getWidth(style)};
        function updateSize() {
            if(document.body.offsetHeight !== height || document.body.offsetWidth !== width) {
                var size = getSize(document.body.firstChild);
                height = size.height;
                width = size.width;
                document.title = height.toString() + ',' + width.toString();
            }
        }
        ${commonScript}
        ${domMutationObserveScript}
    } ());
    `;
}
<|endoftext|>