<commit_before>const {eq, schema, doc, p} = require("prosemirror-test-builder")
const {EditorState, Plugin, TextSelection} = require("prosemirror-state")
const ist = require("ist")

const {history, closeHistory, undo, redo, undoDepth, redoDepth} = require("../dist/history")

let plugin = history()

function mkState(doc, config) {
  let plugins = [config ? history(config) : plugin]
  if (config && config.preserveItems) plugins.push(new Plugin({historyPreserveItems: true}))
  return EditorState.create({schema, plugins, doc})
}

function type(state, text) {
  return state.apply(state.tr.insertText(text))
}
function command(state, command) {
  command(state, tr => state = state.apply(tr))
  return state
}

function compress(state) {
  // NOTE: This is mutating stuff that shouldn't be mutated. Not safe
  // to do outside of these tests.
  plugin.getState(state).done = plugin.getState(state).done.compress()
}

describe("history", () => {
  it("enables undo", () => {
    let state = mkState()
    state = type(state, "a")
    state = type(state, "b")
    ist(state.doc, doc(p("ab")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
  })

  it("enables redo", () => {
    let state = mkState()
    state = type(state, "a")
    state = type(state, "b")
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
    state = command(state, redo)
    ist(state.doc, doc(p("ab")), eq)
  })

  it("tracks multiple levels of history", () => {
    let state = mkState()
    state = type(state, "a")
    state = type(state, "b")
    state = state.apply(state.tr.insertText("c", 1))
    ist(state.doc, doc(p("cab")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("ab")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
    state = command(state, redo)
    ist(state.doc, doc(p("ab")), eq)
    state = command(state, redo)
    ist(state.doc, doc(p("cab")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("ab")), eq)
  })

  it("starts a new event when newGroupDelay elapses", () => {
    let state = mkState(null, {newGroupDelay: 1000})
    state = state.apply(state.tr.insertText("a").setTime(1000))
    state = state.apply(state.tr.insertText("b").setTime(1600))
    ist(undoDepth(state), 1)
    state = state.apply(state.tr.insertText("c").setTime(2700))
    ist(undoDepth(state), 2)
    state = command(state, undo)
    state = state.apply(state.tr.insertText("d").setTime(2800))
    ist(undoDepth(state), 2)
  })

  it("allows changes that aren't part of the history", () => {
    let state = mkState()
    state = type(state, "hello")
    state = state.apply(state.tr.insertText("oops", 1).setMeta("addToHistory", false))
    state = state.apply(state.tr.insertText("!", 10).setMeta("addToHistory", false))
    state = command(state, undo)
    ist(state.doc, doc(p("oops!")), eq)
  })

  it("doesn't get confused by an undo not adding any redo item", () => {
    let state = mkState()
    state = state.apply(state.tr.insertText("foo"))
    state = state.apply(state.tr.replaceWith(1, 4, schema.text("bar")).setMeta("addToHistory", false))
    state = command(state, undo)
    state = command(state, redo)
    ist(state.doc, doc(p("bar")), eq)
  })

  function unsyncedComplex(state, doCompress) {
    state = type(state, "hello")
    state = state.apply(closeHistory(state.tr))
    state = type(state, "!")
    state = state.apply(state.tr.insertText("....", 1).setMeta("addToHistory", false))
    state = state.apply(state.tr.split(3))
    ist(state.doc, doc(p(".."), p("..hello!")), eq)
    state = state.apply(state.tr.split(2).setMeta("addToHistory", false))
    if (doCompress) compress(state)
    state = command(state, undo)
    state = command(state, undo)
    ist(state.doc, doc(p("."), p("...hello")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("."), p("...")), eq)
  }

  it("can handle complex editing sequences", () => {
    unsyncedComplex(mkState(), false)
  })

  it("can handle complex editing sequences with compression", () => {
    unsyncedComplex(mkState(), true)
  })

  it("supports overlapping edits", () => {
    let state = mkState()
    state = type(state, "hello")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.delete(1, 6))
    ist(state.doc, doc(p()), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("hello")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
  })

  it("supports overlapping edits that aren't collapsed", () => {
    let state = mkState()
    state = state.apply(state.tr.insertText("h", 1).setMeta("addToHistory", false))
    state = type(state, "ello")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.delete(1, 6))
    ist(state.doc, doc(p()), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("hello")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("h")), eq)
  })

  it("supports overlapping unsynced deletes", () => {
    let state = mkState()
    state = type(state, "hi")
    state = state.apply(closeHistory(state.tr))
    state = type(state, "hello")
    state = state.apply(state.tr.delete(1, 8).setMeta("addToHistory", false))
    ist(state.doc, doc(p()), eq)
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
  })

  it("can go back and forth through history multiple times", () => {
    let state = mkState()
    state = type(state, "one")
    state = type(state, " two")
    state = state.apply(closeHistory(state.tr))
    state = type(state, " three")
    state = state.apply(state.tr.insertText("zero ", 1))
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.split(1))
    state = state.apply(state.tr.setSelection(TextSelection.create(state.doc, 1)))
    state = type(state, "top")
    for (let i = 0; i < 6; i++) {
      let re = i % 2
      for (let j = 0; j < 4; j++) state = command(state, re ? redo : undo)
      ist(state.doc, re ? doc(p("top"), p("zero one two three")) : doc(p()), eq)
    }
  })

  it("supports non-tracked changes next to tracked changes", () => {
    let state = mkState()
    state = type(state, "o")
    state = state.apply(state.tr.split(1))
    state = state.apply(state.tr.insertText("zzz", 4).setMeta("addToHistory", false))
    state = command(state, undo)
    ist(state.doc, doc(p("zzz")), eq)
  })

  it("can go back and forth through history when preserving items", () => {
    let state = mkState()
    state = type(state, "one")
    state = type(state, " two")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.insertText("xxx", state.selection.head).setMeta("addToHistory", false))
    state = type(state, " three")
    state = state.apply(state.tr.insertText("zero ", 1))
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.split(1))
    state = state.apply(state.tr.setSelection(TextSelection.create(state.doc, 1)))
    state = type(state, "top")
    state = state.apply(state.tr.insertText("yyy", 1).setMeta("addToHistory", false))
    for (let i = 0; i < 3; i++) {
      if (i == 2) compress(state)
      for (let j = 0; j < 4; j++) state = command(state, undo)
      ist(state.doc, doc(p("yyyxxx")), eq)
      for (let j = 0; j < 4; j++) state = command(state, redo)
      ist(state.doc, doc(p("yyytop"), p("zero one twoxxx three")), eq)
    }
  })

  it("restores selection on undo", () => {
    let state = mkState()
    state = type(state, "hi")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.setSelection(TextSelection.create(state.doc, 1, 3)))
    let selection = state.selection
    state = state.apply(state.tr.replaceWith(selection.from, selection.to, schema.text("hello")))
    let selection2 = state.selection
    state = command(state, undo)
    ist(state.selection.eq(selection))
    state = command(state, redo)
    ist(state.selection.eq(selection2))
  })

  it("rebases selection on undo", () => {
    let state = mkState()
    state = type(state, "hi")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.setSelection(TextSelection.create(state.doc, 1, 3)))
    state = state.apply(state.tr.insert(1, schema.text("hello")))
    state = state.apply(state.tr.insert(1, schema.text("---")).setMeta("addToHistory", false))
    state = command(state, undo)
    ist(state.selection.head, 6)
  })

  it("handles change overwriting in item-preserving mode", () => {
    let state = mkState(null, {preserveItems: true})
    state = type(state, "a")
    state = type(state, "b")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.setSelection(TextSelection.create(state.doc, 1, 3)))
    state = type(state, "c")
    state = command(state, undo)
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
  })

  it("supports querying for the undo and redo depth", () => {
    let state = mkState()
    state = type(state, "a")
    ist(undoDepth(state), 1)
    ist(redoDepth(state), 0)
    state = state.apply(state.tr.insertText("b", 1).setMeta("addToHistory", false))
    ist(undoDepth(state), 1)
    ist(redoDepth(state), 0)
    state = command(state, undo)
    ist(undoDepth(state), 0)
    ist(redoDepth(state), 1)
    state = command(state, redo)
    ist(undoDepth(state), 1)
    ist(redoDepth(state), 0)
  })

  it("all functions gracefully handle EditorStates without history", () => {
    let state = new EditorState()
    ist(undoDepth(state), 0)
    ist(redoDepth(state), 0)
    ist(undo(state), false)
    ist(redo(state), false)
  })
})
<commit_msg>Add test for history truncating<commit_after>const {eq, schema, doc, p} = require("prosemirror-test-builder")
const {EditorState, Plugin, TextSelection} = require("prosemirror-state")
const ist = require("ist")

const {history, closeHistory, undo, redo, undoDepth, redoDepth} = require("../dist/history")

let plugin = history()

function mkState(doc, config) {
  let plugins = [config ? history(config) : plugin]
  if (config && config.preserveItems) plugins.push(new Plugin({historyPreserveItems: true}))
  return EditorState.create({schema, plugins, doc})
}

function type(state, text) {
  return state.apply(state.tr.insertText(text))
}
function command(state, command) {
  command(state, tr => state = state.apply(tr))
  return state
}

function compress(state) {
  // NOTE: This is mutating stuff that shouldn't be mutated. Not safe
  // to do outside of these tests.
  plugin.getState(state).done = plugin.getState(state).done.compress()
}

describe("history", () => {
  it("enables undo", () => {
    let state = mkState()
    state = type(state, "a")
    state = type(state, "b")
    ist(state.doc, doc(p("ab")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
  })

  it("enables redo", () => {
    let state = mkState()
    state = type(state, "a")
    state = type(state, "b")
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
    state = command(state, redo)
    ist(state.doc, doc(p("ab")), eq)
  })

  it("tracks multiple levels of history", () => {
    let state = mkState()
    state = type(state, "a")
    state = type(state, "b")
    state = state.apply(state.tr.insertText("c", 1))
    ist(state.doc, doc(p("cab")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("ab")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
    state = command(state, redo)
    ist(state.doc, doc(p("ab")), eq)
    state = command(state, redo)
    ist(state.doc, doc(p("cab")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("ab")), eq)
  })

  it("starts a new event when newGroupDelay elapses", () => {
    let state = mkState(null, {newGroupDelay: 1000})
    state = state.apply(state.tr.insertText("a").setTime(1000))
    state = state.apply(state.tr.insertText("b").setTime(1600))
    ist(undoDepth(state), 1)
    state = state.apply(state.tr.insertText("c").setTime(2700))
    ist(undoDepth(state), 2)
    state = command(state, undo)
    state = state.apply(state.tr.insertText("d").setTime(2800))
    ist(undoDepth(state), 2)
  })

  it("allows changes that aren't part of the history", () => {
    let state = mkState()
    state = type(state, "hello")
    state = state.apply(state.tr.insertText("oops", 1).setMeta("addToHistory", false))
    state = state.apply(state.tr.insertText("!", 10).setMeta("addToHistory", false))
    state = command(state, undo)
    ist(state.doc, doc(p("oops!")), eq)
  })

  it("doesn't get confused by an undo not adding any redo item", () => {
    let state = mkState()
    state = state.apply(state.tr.insertText("foo"))
    state = state.apply(state.tr.replaceWith(1, 4, schema.text("bar")).setMeta("addToHistory", false))
    state = command(state, undo)
    state = command(state, redo)
    ist(state.doc, doc(p("bar")), eq)
  })

  function unsyncedComplex(state, doCompress) {
    state = type(state, "hello")
    state = state.apply(closeHistory(state.tr))
    state = type(state, "!")
    state = state.apply(state.tr.insertText("....", 1).setMeta("addToHistory", false))
    state = state.apply(state.tr.split(3))
    ist(state.doc, doc(p(".."), p("..hello!")), eq)
    state = state.apply(state.tr.split(2).setMeta("addToHistory", false))
    if (doCompress) compress(state)
    state = command(state, undo)
    state = command(state, undo)
    ist(state.doc, doc(p("."), p("...hello")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("."), p("...")), eq)
  }

  it("can handle complex editing sequences", () => {
    unsyncedComplex(mkState(), false)
  })

  it("can handle complex editing sequences with compression", () => {
    unsyncedComplex(mkState(), true)
  })

  it("supports overlapping edits", () => {
    let state = mkState()
    state = type(state, "hello")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.delete(1, 6))
    ist(state.doc, doc(p()), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("hello")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
  })

  it("supports overlapping edits that aren't collapsed", () => {
    let state = mkState()
    state = state.apply(state.tr.insertText("h", 1).setMeta("addToHistory", false))
    state = type(state, "ello")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.delete(1, 6))
    ist(state.doc, doc(p()), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("hello")), eq)
    state = command(state, undo)
    ist(state.doc, doc(p("h")), eq)
  })

  it("supports overlapping unsynced deletes", () => {
    let state = mkState()
    state = type(state, "hi")
    state = state.apply(closeHistory(state.tr))
    state = type(state, "hello")
    state = state.apply(state.tr.delete(1, 8).setMeta("addToHistory", false))
    ist(state.doc, doc(p()), eq)
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
  })

  it("can go back and forth through history multiple times", () => {
    let state = mkState()
    state = type(state, "one")
    state = type(state, " two")
    state = state.apply(closeHistory(state.tr))
    state = type(state, " three")
    state = state.apply(state.tr.insertText("zero ", 1))
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.split(1))
    state = state.apply(state.tr.setSelection(TextSelection.create(state.doc, 1)))
    state = type(state, "top")
    for (let i = 0; i < 6; i++) {
      let re = i % 2
      for (let j = 0; j < 4; j++) state = command(state, re ? redo : undo)
      ist(state.doc, re ? doc(p("top"), p("zero one two three")) : doc(p()), eq)
    }
  })

  it("supports non-tracked changes next to tracked changes", () => {
    let state = mkState()
    state = type(state, "o")
    state = state.apply(state.tr.split(1))
    state = state.apply(state.tr.insertText("zzz", 4).setMeta("addToHistory", false))
    state = command(state, undo)
    ist(state.doc, doc(p("zzz")), eq)
  })

  it("can go back and forth through history when preserving items", () => {
    let state = mkState()
    state = type(state, "one")
    state = type(state, " two")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.insertText("xxx", state.selection.head).setMeta("addToHistory", false))
    state = type(state, " three")
    state = state.apply(state.tr.insertText("zero ", 1))
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.split(1))
    state = state.apply(state.tr.setSelection(TextSelection.create(state.doc, 1)))
    state = type(state, "top")
    state = state.apply(state.tr.insertText("yyy", 1).setMeta("addToHistory", false))
    for (let i = 0; i < 3; i++) {
      if (i == 2) compress(state)
      for (let j = 0; j < 4; j++) state = command(state, undo)
      ist(state.doc, doc(p("yyyxxx")), eq)
      for (let j = 0; j < 4; j++) state = command(state, redo)
      ist(state.doc, doc(p("yyytop"), p("zero one twoxxx three")), eq)
    }
  })

  it("restores selection on undo", () => {
    let state = mkState()
    state = type(state, "hi")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.setSelection(TextSelection.create(state.doc, 1, 3)))
    let selection = state.selection
    state = state.apply(state.tr.replaceWith(selection.from, selection.to, schema.text("hello")))
    let selection2 = state.selection
    state = command(state, undo)
    ist(state.selection.eq(selection))
    state = command(state, redo)
    ist(state.selection.eq(selection2))
  })

  it("rebases selection on undo", () => {
    let state = mkState()
    state = type(state, "hi")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.setSelection(TextSelection.create(state.doc, 1, 3)))
    state = state.apply(state.tr.insert(1, schema.text("hello")))
    state = state.apply(state.tr.insert(1, schema.text("---")).setMeta("addToHistory", false))
    state = command(state, undo)
    ist(state.selection.head, 6)
  })

  it("handles change overwriting in item-preserving mode", () => {
    let state = mkState(null, {preserveItems: true})
    state = type(state, "a")
    state = type(state, "b")
    state = state.apply(closeHistory(state.tr))
    state = state.apply(state.tr.setSelection(TextSelection.create(state.doc, 1, 3)))
    state = type(state, "c")
    state = command(state, undo)
    state = command(state, undo)
    ist(state.doc, doc(p()), eq)
  })

  it("supports querying for the undo and redo depth", () => {
    let state = mkState()
    state = type(state, "a")
    ist(undoDepth(state), 1)
    ist(redoDepth(state), 0)
    state = state.apply(state.tr.insertText("b", 1).setMeta("addToHistory", false))
    ist(undoDepth(state), 1)
    ist(redoDepth(state), 0)
    state = command(state, undo)
    ist(undoDepth(state), 0)
    ist(redoDepth(state), 1)
    state = command(state, redo)
    ist(undoDepth(state), 1)
    ist(redoDepth(state), 0)
  })

  it("all functions gracefully handle EditorStates without history", () => {
    let state = new EditorState()
    ist(undoDepth(state), 0)
    ist(redoDepth(state), 0)
    ist(undo(state), false)
    ist(redo(state), false)
  })

  it("truncates history", () => {
    let state = mkState(null, {depth: 2})
    for (let i = 1; i < 40; ++i) {
      state = type(state, "a")
      state = state.apply(closeHistory(state.tr))
      ist(undoDepth(state), (i - 2) % 21 + 2)
    }
  })
})
<|endoftext|>


<commit_before>var ObjectTest = function(){
	this.spriteID = "image";
	return this;
}
ObjectTest.prototype.Init = function() {
	this.positionX = 30;
	this.positionY = 30;
}
ObjectTest.prototype.Update = function() {
	this.positionX += 1;
	this.positionY += 1;
}
ObjectTest.prototype.Draw = function(d) {
	d.drawImage(document.getElementById(this.spriteID), 0, 0);
}

var ObjectFireBall = function(x, y , angle){
	this.spriteID = "image";
	this.positionX = x;
	this.positionY = y;
	this.angle = angle;
	return this;
}
ObjectFireBall.prototype.Init = function() {
	this.angleRadian = (this.angle * Math.PI)/180;
	this.plusX = Math.cos(this.angleRadian);
	this.plusY = Math.sin(this.angleRadian);
}
ObjectFireBall.prototype.Update = function() {
	this.positionX += this.plusX;
	this.positionY -= this.plusY;
}
ObjectFireBall.prototype.Draw = function(d) {
	d.drawImage(document.getElementById(this.spriteID), 0, 0);
}

var ObjectTestNoOffset = function() {
	this.spriteID = "image";
	return this;
}
ObjectTestNoOffset.prototype.Draw = function(d) {
	d.drawImageNoOffset(document.getElementById(this.spriteID), 15, 15)
}<commit_msg>Added speed to fireball<commit_after>var ObjectTest = function(){
	this.spriteID = "image";
	return this;
}
ObjectTest.prototype.Init = function() {
	this.positionX = 30;
	this.positionY = 30;
}
ObjectTest.prototype.Update = function() {
	this.positionX += 1;
	this.positionY += 1;
}
ObjectTest.prototype.Draw = function(d) {
	d.drawImage(document.getElementById(this.spriteID), 0, 0);
}

var ObjectFireBall = function(x, y , angle, speed){
	this.spriteID = "image";
	this.positionX = x;
	this.positionY = y;
	this.speed = speed;
	this.angle = angle;
	return this;
}
ObjectFireBall.prototype.Init = function() {
	this.angleRadian = (this.angle * Math.PI)/180;
	this.plusX = Math.cos(this.angleRadian);
	this.plusY = Math.sin(this.angleRadian);
}
ObjectFireBall.prototype.Update = function() {
	this.positionX += this.plusX * this.speed;
	this.positionY -= this.plusY * this.speed;
}
ObjectFireBall.prototype.Draw = function(d) {
	d.drawImage(document.getElementById(this.spriteID), 0, 0);
}

var ObjectTestNoOffset = function() {
	this.spriteID = "image";
	return this;
}
ObjectTestNoOffset.prototype.Draw = function(d) {
	d.drawImageNoOffset(document.getElementById(this.spriteID), 15, 15)
}<|endoftext|>


<commit_before>var Server = require('../lib/server');
var utils = require('ssh2-streams').utils;

var semver = require('semver');

var fs = require('fs');
var crypto = require('crypto');
var path = require('path');
var join = path.join;
var assert = require('assert');
var spawn = require('child_process').spawn;

var t = -1;
var group = path.basename(__filename, '.js') + '/';
var fixturesdir = join(__dirname, 'fixtures');

var CLIENT_TIMEOUT = 5000;
var USER = 'nodejs';
var HOST_KEY_RSA = fs.readFileSync(join(fixturesdir, 'ssh_host_rsa_key'));
var HOST_KEY_DSA = fs.readFileSync(join(fixturesdir, 'ssh_host_dsa_key'));
var HOST_KEY_ECDSA = fs.readFileSync(join(fixturesdir, 'ssh_host_ecdsa_key'));
var CLIENT_KEY_RSA_PATH = join(fixturesdir, 'id_rsa');
var CLIENT_KEY_RSA = fs.readFileSync(CLIENT_KEY_RSA_PATH);
var CLIENT_KEY_RSA_PUB = utils.genPublicKey(utils.parseKey(CLIENT_KEY_RSA));
var CLIENT_KEY_DSA_PATH = join(fixturesdir, 'id_dsa');
var CLIENT_KEY_DSA = fs.readFileSync(CLIENT_KEY_DSA_PATH);
var CLIENT_KEY_DSA_PUB = utils.genPublicKey(utils.parseKey(CLIENT_KEY_DSA));
if (semver.gte(process.version, '5.2.0')) {
  var CLIENT_KEY_ECDSA_PATH = join(fixturesdir, 'id_ecdsa');
  var CLIENT_KEY_ECDSA = fs.readFileSync(CLIENT_KEY_ECDSA_PATH);
  var CLIENT_KEY_ECDSA_PUB = utils.genPublicKey(
    utils.parseKey(CLIENT_KEY_ECDSA)
  );
}
var DEBUG = false;

// Fix file modes to avoid OpenSSH client complaints about keys' permissions
fs.readdirSync(fixturesdir).forEach(function(file) {
  fs.chmodSync(join(fixturesdir, file), '0600');
});

var tests = [
  { run: function() {
      var what = this.what;
      var server;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_RSA_PATH },
        { hostKeys: [HOST_KEY_RSA] }
      );

      server.on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          if (ctx.method === 'none')
            return ctx.reject();
          assert(ctx.method === 'publickey',
                 makeMsg(what, 'Unexpected auth method: ' + ctx.method));
          assert(ctx.username === USER,
                 makeMsg(what, 'Unexpected username: ' + ctx.username));
          assert(ctx.key.algo === 'ssh-rsa',
                 makeMsg(what, 'Unexpected key algo: ' + ctx.key.algo));
          assert.deepEqual(CLIENT_KEY_RSA_PUB.public,
                           ctx.key.data,
                           makeMsg(what, 'Public key mismatch'));
          if (ctx.signature) {
            var verifier = crypto.createVerify('RSA-SHA1');
            var pem = CLIENT_KEY_RSA_PUB.publicOrig;
            verifier.update(ctx.blob);
            assert(verifier.verify(pem, ctx.signature),
                   makeMsg(what, 'Could not verify PK signature'));
            ctx.accept();
          } else
            ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            if (session) {
              session.on('exec', function(accept, reject) {
                var stream = accept();
                if (stream) {
                  stream.exit(0);
                  stream.end();
                }
              }).on('pty', function(accept, reject) {
                accept && accept();
              });
            }
          });
        });
      });
    },
    what: 'Authenticate with an RSA key'
  },
  { run: function() {
      var what = this.what;
      var server;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_DSA_PATH },
        { hostKeys: [HOST_KEY_RSA] }
      );

      server.on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          if (ctx.method === 'none')
            return ctx.reject();
          assert(ctx.method === 'publickey',
                 makeMsg(what, 'Unexpected auth method: ' + ctx.method));
          assert(ctx.username === USER,
                 makeMsg(what, 'Unexpected username: ' + ctx.username));
          assert(ctx.key.algo === 'ssh-dss',
                 makeMsg(what, 'Unexpected key algo: ' + ctx.key.algo));
          assert.deepEqual(CLIENT_KEY_DSA_PUB.public,
                           ctx.key.data,
                           makeMsg(what, 'Public key mismatch'));
          if (ctx.signature) {
            var verifier = crypto.createVerify('DSA-SHA1');
            var pem = CLIENT_KEY_DSA_PUB.publicOrig;
            verifier.update(ctx.blob);
            assert(verifier.verify(pem, ctx.signature),
                   makeMsg(what, 'Could not verify PK signature'));
            ctx.accept();
          } else
            ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            if (session) {
              session.on('exec', function(accept, reject) {
                var stream = accept();
                if (stream) {
                  stream.exit(0);
                  stream.end();
                }
              }).on('pty', function(accept, reject) {
                accept && accept();
              });
            }
          });
        });
      });
    },
    what: 'Authenticate with a DSA key'
  },
  { run: function() {
      if (semver.lt(process.version, '5.2.0'))
        return next();
      var what = this.what;
      var server;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_ECDSA_PATH },
        { hostKeys: [HOST_KEY_RSA] }
      );

      server.on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          if (ctx.method === 'none')
            return ctx.reject();
          assert(ctx.method === 'publickey',
                 makeMsg(what, 'Unexpected auth method: ' + ctx.method));
          assert(ctx.username === USER,
                 makeMsg(what, 'Unexpected username: ' + ctx.username));
          assert(ctx.key.algo === 'ecdsa-sha2-nistp256',
                 makeMsg(what, 'Unexpected key algo: ' + ctx.key.algo));
          assert.deepEqual(CLIENT_KEY_ECDSA_PUB.public,
                           ctx.key.data,
                           makeMsg(what, 'Public key mismatch'));
          if (ctx.signature) {
            var verifier = crypto.createVerify('sha256');
            var pem = CLIENT_KEY_ECDSA_PUB.publicOrig;
            verifier.update(ctx.blob);
            assert(verifier.verify(pem, ctx.signature),
                   makeMsg(what, 'Could not verify PK signature'));
            ctx.accept();
          } else
            ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            if (session) {
              session.on('exec', function(accept, reject) {
                var stream = accept();
                if (stream) {
                  stream.exit(0);
                  stream.end();
                }
              }).on('pty', function(accept, reject) {
                accept && accept();
              });
            }
          });
        });
      });
    },
    what: 'Authenticate with a ECDSA key'
  },
  { run: function() {
      var server;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_RSA_PATH },
        { hostKeys: [HOST_KEY_DSA] }
      );

      server.on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            if (session) {
              session.on('exec', function(accept, reject) {
                var stream = accept();
                if (stream) {
                  stream.exit(0);
                  stream.end();
                }
              }).on('pty', function(accept, reject) {
                accept && accept();
              });
            }
          });
        });
      });
    },
    what: 'Server with DSA host key'
  },
  { run: function() {
      if (semver.lt(process.version, '5.2.0'))
        return next();
      var server;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_RSA_PATH },
        { hostKeys: [HOST_KEY_ECDSA] }
      );

      server.on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            if (session) {
              session.on('exec', function(accept, reject) {
                var stream = accept();
                if (stream) {
                  stream.exit(0);
                  stream.end();
                }
              }).on('pty', function(accept, reject) {
                accept && accept();
              });
            }
          });
        });
      });
    },
    what: 'Server with ECDSA host key'
  },
  { run: function() {
      var server;
      var what = this.what;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_RSA_PATH },
        { hostKeys: [HOST_KEY_RSA] }
      );

      server.on('_child', function(childProc) {
        childProc.stderr.once('data', function(data) {
          childProc.stdin.end();
        });
        childProc.stdin.write('ping');
      }).on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            assert(session, makeMsg(what, 'Missing session'));
            session.on('exec', function(accept, reject) {
              var stream = accept();
              assert(stream, makeMsg(what, 'Missing exec stream'));
              stream.stdin.on('data', function(data) {
                stream.stdout.write('pong on stdout');
                stream.stderr.write('pong on stderr');
              }).on('end', function() {
                stream.stdout.write('pong on stdout');
                stream.stderr.write('pong on stderr');
                stream.exit(0);
                stream.close();
              });
            }).on('pty', function(accept, reject) {
              accept && accept();
            });
          });
        });
      });
    },
    what: 'Server closes stdin too early'
  },
];

function setup(self, clientcfg, servercfg) {
  self.state = {
    serverReady: false,
    clientClose: false,
    serverClose: false
  };

  var client;
  var server = new Server(servercfg);

  server.on('error', onError)
        .on('connection', function(conn) {
          conn.on('error', onError)
              .on('ready', onReady);
          server.close();
        })
        .on('close', onClose);

  function onError(err) {
    var which = (arguments.length >= 3 ? 'client' : 'server');
    assert(false, makeMsg(self.what, 'Unexpected ' + which + ' error: ' + err));
  }
  function onReady() {
    assert(!self.state.serverReady,
           makeMsg(self.what, 'Received multiple ready events for server'));
    self.state.serverReady = true;
    self.onReady && self.onReady();
  }
  function onClose() {
    if (arguments.length >= 3) {
      assert(!self.state.clientClose,
             makeMsg(self.what, 'Received multiple close events for client'));
      self.state.clientClose = true;
    } else {
      assert(!self.state.serverClose,
             makeMsg(self.what, 'Received multiple close events for server'));
      self.state.serverClose = true;
    }
    if (self.state.clientClose && self.state.serverClose)
      next();
  }

  process.nextTick(function() {
    server.listen(0, 'localhost', function() {
      var cmd = 'ssh';
      var args = ['-o', 'UserKnownHostsFile=/dev/null',
                  '-o', 'StrictHostKeyChecking=no',
                  '-o', 'CheckHostIP=no',
                  '-o', 'ConnectTimeout=3',
                  '-o', 'GlobalKnownHostsFile=/dev/null',
                  '-o', 'GSSAPIAuthentication=no',
                  '-o', 'IdentitiesOnly=yes',
                  '-o', 'BatchMode=yes',
                  '-o', 'VerifyHostKeyDNS=no',
                  '-vvvvvv',
                  '-T',
                  '-o', 'KbdInteractiveAuthentication=no',
                  '-o', 'HostbasedAuthentication=no',
                  '-o', 'PasswordAuthentication=no',
                  '-o', 'PubkeyAuthentication=yes',
                  '-o', 'PreferredAuthentications=publickey'];
      if (clientcfg.privateKeyPath)
        args.push('-o', 'IdentityFile=' + clientcfg.privateKeyPath);
      args.push('-p', server.address().port.toString(),
                '-l', USER,
                'localhost',
                'uptime');

      client = spawn(cmd, args);
      server.emit('_child', client);
      if (DEBUG) {
        client.stdout.pipe(process.stdout);
        client.stderr.pipe(process.stderr);
      } else {
        client.stdout.resume();
        client.stderr.resume();
      }
      client.on('error', function(err) {
        onError(err, null, null);
      }).on('exit', function(code) {
        clearTimeout(client.timer);
        if (code !== 0)
          return onError(new Error('Non-zero exit code ' + code), null, null);
        onClose(null, null, null);
      });

      client.timer = setTimeout(function() {
        assert(false, makeMsg(self.what, 'Client timeout'));
      }, CLIENT_TIMEOUT);
    });
  });
  return server;
}

function next() {
  if (Array.isArray(process._events.exit))
    process._events.exit = process._events.exit[1];
  if (++t === tests.length)
    return;

  var v = tests[t];
  v.run.call(v);
}

function makeMsg(what, msg) {
  return '[' + group + what + ']: ' + msg;
}

process.once('uncaughtException', function(err) {
  if (t > -1 && !/(?:^|\n)AssertionError: /i.test(''+err))
    console.log(makeMsg(tests[t].what, 'Unexpected Exception:'));
  throw err;
});
process.once('exit', function() {
  assert(t === tests.length,
         makeMsg('_exit',
                 'Only finished ' + t + '/' + tests.length + ' tests'));
});

next();
<commit_msg>test: re-enable OpenSSH DSS/DSA key support<commit_after>var Server = require('../lib/server');
var utils = require('ssh2-streams').utils;

var semver = require('semver');

var fs = require('fs');
var crypto = require('crypto');
var path = require('path');
var join = path.join;
var assert = require('assert');
var spawn = require('child_process').spawn;

var t = -1;
var group = path.basename(__filename, '.js') + '/';
var fixturesdir = join(__dirname, 'fixtures');

var CLIENT_TIMEOUT = 5000;
var USER = 'nodejs';
var HOST_KEY_RSA = fs.readFileSync(join(fixturesdir, 'ssh_host_rsa_key'));
var HOST_KEY_DSA = fs.readFileSync(join(fixturesdir, 'ssh_host_dsa_key'));
var HOST_KEY_ECDSA = fs.readFileSync(join(fixturesdir, 'ssh_host_ecdsa_key'));
var CLIENT_KEY_RSA_PATH = join(fixturesdir, 'id_rsa');
var CLIENT_KEY_RSA = fs.readFileSync(CLIENT_KEY_RSA_PATH);
var CLIENT_KEY_RSA_PUB = utils.genPublicKey(utils.parseKey(CLIENT_KEY_RSA));
var CLIENT_KEY_DSA_PATH = join(fixturesdir, 'id_dsa');
var CLIENT_KEY_DSA = fs.readFileSync(CLIENT_KEY_DSA_PATH);
var CLIENT_KEY_DSA_PUB = utils.genPublicKey(utils.parseKey(CLIENT_KEY_DSA));
if (semver.gte(process.version, '5.2.0')) {
  var CLIENT_KEY_ECDSA_PATH = join(fixturesdir, 'id_ecdsa');
  var CLIENT_KEY_ECDSA = fs.readFileSync(CLIENT_KEY_ECDSA_PATH);
  var CLIENT_KEY_ECDSA_PUB = utils.genPublicKey(
    utils.parseKey(CLIENT_KEY_ECDSA)
  );
}
var DEBUG = false;

// Fix file modes to avoid OpenSSH client complaints about keys' permissions
fs.readdirSync(fixturesdir).forEach(function(file) {
  fs.chmodSync(join(fixturesdir, file), '0600');
});

var tests = [
  { run: function() {
      var what = this.what;
      var server;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_RSA_PATH },
        { hostKeys: [HOST_KEY_RSA] }
      );

      server.on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          if (ctx.method === 'none')
            return ctx.reject();
          assert(ctx.method === 'publickey',
                 makeMsg(what, 'Unexpected auth method: ' + ctx.method));
          assert(ctx.username === USER,
                 makeMsg(what, 'Unexpected username: ' + ctx.username));
          assert(ctx.key.algo === 'ssh-rsa',
                 makeMsg(what, 'Unexpected key algo: ' + ctx.key.algo));
          assert.deepEqual(CLIENT_KEY_RSA_PUB.public,
                           ctx.key.data,
                           makeMsg(what, 'Public key mismatch'));
          if (ctx.signature) {
            var verifier = crypto.createVerify('RSA-SHA1');
            var pem = CLIENT_KEY_RSA_PUB.publicOrig;
            verifier.update(ctx.blob);
            assert(verifier.verify(pem, ctx.signature),
                   makeMsg(what, 'Could not verify PK signature'));
            ctx.accept();
          } else
            ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            if (session) {
              session.on('exec', function(accept, reject) {
                var stream = accept();
                if (stream) {
                  stream.exit(0);
                  stream.end();
                }
              }).on('pty', function(accept, reject) {
                accept && accept();
              });
            }
          });
        });
      });
    },
    what: 'Authenticate with an RSA key'
  },
  { run: function() {
      var what = this.what;
      var server;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_DSA_PATH },
        { hostKeys: [HOST_KEY_RSA] }
      );

      server.on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          if (ctx.method === 'none')
            return ctx.reject();
          assert(ctx.method === 'publickey',
                 makeMsg(what, 'Unexpected auth method: ' + ctx.method));
          assert(ctx.username === USER,
                 makeMsg(what, 'Unexpected username: ' + ctx.username));
          assert(ctx.key.algo === 'ssh-dss',
                 makeMsg(what, 'Unexpected key algo: ' + ctx.key.algo));
          assert.deepEqual(CLIENT_KEY_DSA_PUB.public,
                           ctx.key.data,
                           makeMsg(what, 'Public key mismatch'));
          if (ctx.signature) {
            var verifier = crypto.createVerify('DSA-SHA1');
            var pem = CLIENT_KEY_DSA_PUB.publicOrig;
            verifier.update(ctx.blob);
            assert(verifier.verify(pem, ctx.signature),
                   makeMsg(what, 'Could not verify PK signature'));
            ctx.accept();
          } else
            ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            if (session) {
              session.on('exec', function(accept, reject) {
                var stream = accept();
                if (stream) {
                  stream.exit(0);
                  stream.end();
                }
              }).on('pty', function(accept, reject) {
                accept && accept();
              });
            }
          });
        });
      });
    },
    what: 'Authenticate with a DSA key'
  },
  { run: function() {
      if (semver.lt(process.version, '5.2.0'))
        return next();
      var what = this.what;
      var server;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_ECDSA_PATH },
        { hostKeys: [HOST_KEY_RSA] }
      );

      server.on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          if (ctx.method === 'none')
            return ctx.reject();
          assert(ctx.method === 'publickey',
                 makeMsg(what, 'Unexpected auth method: ' + ctx.method));
          assert(ctx.username === USER,
                 makeMsg(what, 'Unexpected username: ' + ctx.username));
          assert(ctx.key.algo === 'ecdsa-sha2-nistp256',
                 makeMsg(what, 'Unexpected key algo: ' + ctx.key.algo));
          assert.deepEqual(CLIENT_KEY_ECDSA_PUB.public,
                           ctx.key.data,
                           makeMsg(what, 'Public key mismatch'));
          if (ctx.signature) {
            var verifier = crypto.createVerify('sha256');
            var pem = CLIENT_KEY_ECDSA_PUB.publicOrig;
            verifier.update(ctx.blob);
            assert(verifier.verify(pem, ctx.signature),
                   makeMsg(what, 'Could not verify PK signature'));
            ctx.accept();
          } else
            ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            if (session) {
              session.on('exec', function(accept, reject) {
                var stream = accept();
                if (stream) {
                  stream.exit(0);
                  stream.end();
                }
              }).on('pty', function(accept, reject) {
                accept && accept();
              });
            }
          });
        });
      });
    },
    what: 'Authenticate with a ECDSA key'
  },
  { run: function() {
      var server;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_RSA_PATH },
        { hostKeys: [HOST_KEY_DSA] }
      );

      server.on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            if (session) {
              session.on('exec', function(accept, reject) {
                var stream = accept();
                if (stream) {
                  stream.exit(0);
                  stream.end();
                }
              }).on('pty', function(accept, reject) {
                accept && accept();
              });
            }
          });
        });
      });
    },
    what: 'Server with DSA host key'
  },
  { run: function() {
      if (semver.lt(process.version, '5.2.0'))
        return next();
      var server;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_RSA_PATH },
        { hostKeys: [HOST_KEY_ECDSA] }
      );

      server.on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            if (session) {
              session.on('exec', function(accept, reject) {
                var stream = accept();
                if (stream) {
                  stream.exit(0);
                  stream.end();
                }
              }).on('pty', function(accept, reject) {
                accept && accept();
              });
            }
          });
        });
      });
    },
    what: 'Server with ECDSA host key'
  },
  { run: function() {
      var server;
      var what = this.what;

      server = setup(
        this,
        { privateKeyPath: CLIENT_KEY_RSA_PATH },
        { hostKeys: [HOST_KEY_RSA] }
      );

      server.on('_child', function(childProc) {
        childProc.stderr.once('data', function(data) {
          childProc.stdin.end();
        });
        childProc.stdin.write('ping');
      }).on('connection', function(conn) {
        conn.on('authentication', function(ctx) {
          ctx.accept();
        }).on('ready', function() {
          conn.on('session', function(accept, reject) {
            var session = accept();
            assert(session, makeMsg(what, 'Missing session'));
            session.on('exec', function(accept, reject) {
              var stream = accept();
              assert(stream, makeMsg(what, 'Missing exec stream'));
              stream.stdin.on('data', function(data) {
                stream.stdout.write('pong on stdout');
                stream.stderr.write('pong on stderr');
              }).on('end', function() {
                stream.stdout.write('pong on stdout');
                stream.stderr.write('pong on stderr');
                stream.exit(0);
                stream.close();
              });
            }).on('pty', function(accept, reject) {
              accept && accept();
            });
          });
        });
      });
    },
    what: 'Server closes stdin too early'
  },
];

function setup(self, clientcfg, servercfg) {
  self.state = {
    serverReady: false,
    clientClose: false,
    serverClose: false
  };

  var client;
  var server = new Server(servercfg);

  server.on('error', onError)
        .on('connection', function(conn) {
          conn.on('error', onError)
              .on('ready', onReady);
          server.close();
        })
        .on('close', onClose);

  function onError(err) {
    var which = (arguments.length >= 3 ? 'client' : 'server');
    assert(false, makeMsg(self.what, 'Unexpected ' + which + ' error: ' + err));
  }
  function onReady() {
    assert(!self.state.serverReady,
           makeMsg(self.what, 'Received multiple ready events for server'));
    self.state.serverReady = true;
    self.onReady && self.onReady();
  }
  function onClose() {
    if (arguments.length >= 3) {
      assert(!self.state.clientClose,
             makeMsg(self.what, 'Received multiple close events for client'));
      self.state.clientClose = true;
    } else {
      assert(!self.state.serverClose,
             makeMsg(self.what, 'Received multiple close events for server'));
      self.state.serverClose = true;
    }
    if (self.state.clientClose && self.state.serverClose)
      next();
  }

  process.nextTick(function() {
    server.listen(0, 'localhost', function() {
      var cmd = 'ssh';
      var args = ['-o', 'UserKnownHostsFile=/dev/null',
                  '-o', 'StrictHostKeyChecking=no',
                  '-o', 'CheckHostIP=no',
                  '-o', 'ConnectTimeout=3',
                  '-o', 'GlobalKnownHostsFile=/dev/null',
                  '-o', 'GSSAPIAuthentication=no',
                  '-o', 'IdentitiesOnly=yes',
                  '-o', 'BatchMode=yes',
                  '-o', 'VerifyHostKeyDNS=no',

                  // OpenSSH 7.0 disables DSS/DSA host and user key support by
                  // default, so we explicitly enable it here
                  '-o', 'HostKeyAlgorithms=+ssh-dss',

                  '-vvvvvv',
                  '-T',
                  '-o', 'KbdInteractiveAuthentication=no',
                  '-o', 'HostbasedAuthentication=no',
                  '-o', 'PasswordAuthentication=no',
                  '-o', 'PubkeyAuthentication=yes',
                  '-o', 'PreferredAuthentications=publickey'];
      if (clientcfg.privateKeyPath)
        args.push('-o', 'IdentityFile=' + clientcfg.privateKeyPath);
      args.push('-p', server.address().port.toString(),
                '-l', USER,
                'localhost',
                'uptime');

      client = spawn(cmd, args);
      server.emit('_child', client);
      if (DEBUG) {
        client.stdout.pipe(process.stdout);
        client.stderr.pipe(process.stderr);
      } else {
        client.stdout.resume();
        client.stderr.resume();
      }
      client.on('error', function(err) {
        onError(err, null, null);
      }).on('exit', function(code) {
        clearTimeout(client.timer);
        if (code !== 0)
          return onError(new Error('Non-zero exit code ' + code), null, null);
        onClose(null, null, null);
      });

      client.timer = setTimeout(function() {
        assert(false, makeMsg(self.what, 'Client timeout'));
      }, CLIENT_TIMEOUT);
    });
  });
  return server;
}

function next() {
  if (Array.isArray(process._events.exit))
    process._events.exit = process._events.exit[1];
  if (++t === tests.length)
    return;

  var v = tests[t];
  v.run.call(v);
}

function makeMsg(what, msg) {
  return '[' + group + what + ']: ' + msg;
}

process.once('uncaughtException', function(err) {
  if (t > -1 && !/(?:^|\n)AssertionError: /i.test(''+err))
    console.log(makeMsg(tests[t].what, 'Unexpected Exception:'));
  throw err;
});
process.once('exit', function() {
  assert(t === tests.length,
         makeMsg('_exit',
                 'Only finished ' + t + '/' + tests.length + ' tests'));
});

next();
<|endoftext|>


<commit_before>/*
 test.calendar.js
 TODO event test
 */
var myPalette;
/* ax5.calendar.setConfig */
describe('ax5.palette TEST', function () {

    var tmpl = '

';

    $(document.body).append(tmpl);

    it('check palette type', function (done) {
        done(typeof new ax5.ui.palette() == "object" ? "" : "check type error");
    });

    it('palette setConfig', function (done) {
        myPalette = new ax5.ui.palette();
        myPalette.setConfig({
            target: $('[data-ax5palette="01"]'),
            onClick: function () {
                alert('success');
            }
        });

        done();
    });

    it('palette setSelectedColor', function (done) {
        let color = "4a0066";
        setTimeout(function () {
            done(myPalette.setSelectedColor(color).colors[5]._selectedColor == color ? "" : "setSelectedColor error");
        }, myPalette.config.animateTime);
    });
});<commit_msg>update test code<commit_after>/*
 test.calendar.js
 TODO event test
 */

/* ax5.calendar.setConfig */
describe('ax5.palette TEST', function () {
    var myPalette;
    var tmpl = '

';

    $(document.body).append(tmpl);

    it('check palette type', function (done) {
        done(typeof new ax5.ui.palette() == "object" ? "" : "check type error");
    });

    it('palette setConfig', function (done) {
        myPalette = new ax5.ui.palette();
        myPalette.setConfig({
            target: $('[data-ax5palette="01"]'),
            onClick: function () {
                alert('success');
            }
        });

        done();
    });

    it('palette setSelectedColor', function (done) {
        let color = "4a0066";
        setTimeout(function () {
            done(myPalette.setSelectedColor(color).colors[5]._selectedColor == color ? "" : "setSelectedColor error");
        }, myPalette.config.animateTime);
    });
});<|endoftext|>


<commit_before>/*jslint node: true */
/*globals suite, test, setup, suiteSetup, suiteTeardown, done, teardown */
'use strict';

var server = require('../web.js');
var assert = require('assert');
var util = require('util');
var request = require('request');
var should = require('should');

var settings = require('../settings-test.js');

var BASEURL = 'http://localhost:' + settings.port + '/api';

function shouldBeParcel(item) {
  item.should.have.property('centroid');
  item.centroid.should.have.property('type');
  item.centroid.type.should.equal('Point');
  item.centroid.should.have.property('coordinates');
  item.centroid.coordinates.should.have.lengthOf(2);
  item.should.have.property('parcelId');
  item.should.have.property('polygon');
  item.polygon.should.have.property('type');
  item.polygon.type.should.equal('Polygon');
  item.polygon.should.have.property('coordinates');
  item.polygon.coordinates.should.be.an.instanceOf(Array);
  item.should.have.property('address');
}

suite('Parcels', function () {
  suiteSetup(function (done) {
    server.run(settings, done);
  });

  suiteTeardown(function () {
    server.stop();
  });

  suite('GET', function () {
    test('Get parcels inside a bounding box', function (done) {
      // lower-left longitude, lower-left latitude, upper-right longitude,
      // upper-right latitude
      request({
        url: BASEURL + '/parcels?bbox=-83.0805,42.336,-83.08,42.34'
      }, function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.length.should.be.above(40);
        var i;
        for (i = 0; i < parsed.length; i += 1) {
          shouldBeParcel(parsed[i]);
        }

        done();
      });
    });

    test('Do not allow unbounded parcel queries', function (done) {
      request({url: BASEURL + '/parcels'}, function (error, response, body) {
        should.not.exist(error);
        // We should get "413 Request Entity Too Large" if we don't specify a
        // bounding box.
        response.statusCode.should.equal(413);

        done();
      });
    });

    test('Get parcels at a point', function (done) {
      request({
        url: BASEURL + '/parcels?lon=-83.08076&lat=42.338'
      }, function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        // Expect an array, since condos could have parcels that overlap.
        parsed.should.be.an.instanceOf(Array);
        parsed.length.should.be.above(0);
        var i;
        for (i = 0; i < parsed.length; i += 1) {
          shouldBeParcel(parsed[i]);
        }

        done();
      });
    });

    test('Do not allow bounding box with lat-lon query', function (done) {
      request({
        url: BASEURL + '/parcels?bbox=-83.0805,42.336,-83.08,42.34&lon=-83.08076&lat=42.338'
      }, function (error, response, body) {
        should.not.exist(error);
        // We should get "400 Bad Request" if we specify both a bounding box
        // and a point.
        response.statusCode.should.equal(400);

        done();
      });
    });

    test('Point query with no results', function (done) {
      // lon=105.436096, lat=-6.152738 is in the water, in Indonesia, so we
      // should not find any parcels.
      request({
        url: BASEURL + '/parcels?lon=105.436096&lat=-6.152738'
      }, function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json

        var parsed = JSON.parse(body);
        parsed.should.be.an.instanceOf(Array);
        parsed.should.have.lengthOf(0);

        done();
      });
    });

    test('Bounding box query with no results', function (done) {
      // bounding box 105.43,-6.154,105.44,-6.144 is in the water, in
      // Indonesia, so we should not find any parcels.
      request({
        url: BASEURL + '/parcels?bbox=105.43,-6.154,105.44,-6.144'
      }, function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json

        var parsed = JSON.parse(body);
        parsed.should.be.an.instanceOf(Array);
        parsed.should.have.lengthOf(0);

        done();
      });
    });
  });
});

<commit_msg>Parcel data is now multipolygon<commit_after>/*jslint node: true */
/*globals suite, test, setup, suiteSetup, suiteTeardown, done, teardown */
'use strict';

var server = require('../web.js');
var assert = require('assert');
var util = require('util');
var request = require('request');
var should = require('should');

var settings = require('../settings-test.js');

var BASEURL = 'http://localhost:' + settings.port + '/api';

function shouldBeParcel(item) {
  item.should.have.property('centroid');
  item.centroid.should.have.property('type');
  item.centroid.type.should.equal('Point');
  item.centroid.should.have.property('coordinates');
  item.centroid.coordinates.should.have.lengthOf(2);
  item.should.have.property('parcelId');
  item.should.have.property('polygon');
  item.polygon.should.have.property('type');
  item.polygon.type.should.equal('MultiPolygon');
  item.polygon.should.have.property('coordinates');
  item.polygon.coordinates.should.be.an.instanceOf(Array);
  item.should.have.property('address');
}

suite('Parcels', function () {
  suiteSetup(function (done) {
    server.run(settings, done);
  });

  suiteTeardown(function () {
    server.stop();
  });

  suite('GET', function () {
    test('Get parcels inside a bounding box', function (done) {
      // lower-left longitude, lower-left latitude, upper-right longitude,
      // upper-right latitude
      request({
        url: BASEURL + '/parcels?bbox=-83.0805,42.336,-83.08,42.34'
      }, function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        parsed.length.should.be.above(40);
        var i;
        for (i = 0; i < parsed.length; i += 1) {
          shouldBeParcel(parsed[i]);
        }

        done();
      });
    });

    test('Do not allow unbounded parcel queries', function (done) {
      request({url: BASEURL + '/parcels'}, function (error, response, body) {
        should.not.exist(error);
        // We should get "413 Request Entity Too Large" if we don't specify a
        // bounding box.
        response.statusCode.should.equal(413);

        done();
      });
    });

    test('Get parcels at a point', function (done) {
      request({
        url: BASEURL + '/parcels?lon=-83.08076&lat=42.338'
      }, function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json;

        var parsed = JSON.parse(body);
        // Expect an array, since condos could have parcels that overlap.
        parsed.should.be.an.instanceOf(Array);
        parsed.length.should.be.above(0);
        var i;
        for (i = 0; i < parsed.length; i += 1) {
          shouldBeParcel(parsed[i]);
        }

        done();
      });
    });

    test('Do not allow bounding box with lat-lon query', function (done) {
      request({
        url: BASEURL + '/parcels?bbox=-83.0805,42.336,-83.08,42.34&lon=-83.08076&lat=42.338'
      }, function (error, response, body) {
        should.not.exist(error);
        // We should get "400 Bad Request" if we specify both a bounding box
        // and a point.
        response.statusCode.should.equal(400);

        done();
      });
    });

    test('Point query with no results', function (done) {
      // lon=105.436096, lat=-6.152738 is in the water, in Indonesia, so we
      // should not find any parcels.
      request({
        url: BASEURL + '/parcels?lon=105.436096&lat=-6.152738'
      }, function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json

        var parsed = JSON.parse(body);
        parsed.should.be.an.instanceOf(Array);
        parsed.should.have.lengthOf(0);

        done();
      });
    });

    test('Bounding box query with no results', function (done) {
      // bounding box 105.43,-6.154,105.44,-6.144 is in the water, in
      // Indonesia, so we should not find any parcels.
      request({
        url: BASEURL + '/parcels?bbox=105.43,-6.154,105.44,-6.144'
      }, function (error, response, body) {
        should.not.exist(error);
        response.statusCode.should.equal(200);
        response.should.be.json

        var parsed = JSON.parse(body);
        parsed.should.be.an.instanceOf(Array);
        parsed.should.have.lengthOf(0);

        done();
      });
    });
  });
});

<|endoftext|>


<commit_before>var debug       = require('debug')('amqp10-test_connection'),
    should      = require('should'),
    builder     = require('buffer-builder'),

    constants   = require('../lib/constants'),

    Connection  = require('../lib/connection'),
    Session     = require('../lib/session'),

    AMQPError   = require('../lib/types/amqp_error'),

    AttachFrame = require('../lib/frames/attach_frame'),
    BeginFrame  = require('../lib/frames/begin_frame'),
    CloseFrame  = require('../lib/frames/close_frame'),
    EndFrame    = require('../lib/frames/end_frame'),
    OpenFrame   = require('../lib/frames/open_frame'),

    MockServer  = require('./mock_amqp');

function openBuf(options) {
    var open = new OpenFrame(options || { containerId: 'test', hostname: 'localhost' });
    return open.outgoing();
}

function beginBuf(options) {
    var opts = { nextOutgoingId: 1, incomingWindow: 100, outgoingWindow: 100 };
    for (var prop in options) { opts[prop] = options[prop]; }
    var begin = new BeginFrame(opts);
    return begin.outgoing();
}

function endBuf(err) {
    var end = new EndFrame(err);
    return end.outgoing();
}

function closeBuf(err) {
    var close = new CloseFrame(err);
    return close.outgoing();
}

describe('Session', function() {
    var assertTransitions = function(actual, expected) {
        actual.length.should.eql(expected.length-1, "Wrong number of state transitions: Actual " + JSON.stringify(actual) + " vs. Expected " + JSON.stringify(expected));
        for (var idx = 0; idx < expected.length - 1; ++idx) {
            var curTransition = expected[idx] + '=>' + expected[idx+1];
            actual[idx].should.eql(curTransition, "Wrong transition at step "+idx);
        }
    };

    describe('#begin()', function() {
        // NOTE: Only works if you have a local AMQP server running
        /*
         it('should connect to activemq', function(done) {
         this.timeout(0);
         var conn = new Connection({ containerId: 'test', hostname: 'localhost' });
         conn.open('amqp://localhost/');
         setTimeout(function() {
         conn.close();
         done();
         }, 5000);
         });
         */
        var server = null;

        afterEach(function (done) {
            if (server) {
                server.teardown();
                server = null;
            }
            done();
        });

        it('should go through begin/end cycle as asked', function(done) {
            server = new MockServer();
            server.setSequence([ constants.amqpVersion, openBuf(), beginBuf(), endBuf(), closeBuf() ],
                [ constants.amqpVersion, openBuf(), beginBuf({ remoteChannel: 5 }), [ true, endBuf(new AMQPError(AMQPError.ConnectionForced, 'test')) ], [ true, closeBuf()] ]);
            var conn = new Connection({ containerId: 'test', hostname: 'localhost' });
            server.setup(conn);
            var transitions = [];
            var sessionTransitions = [];
            var recordTransitions = function(evt, oldS, newS) { transitions.push(oldS+'=>'+newS); };
            var recordSessionTransitions = function(evt, oldS, newS) { sessionTransitions.push(oldS+'=>'+newS); };
            conn.connSM.bind(recordTransitions);
            conn.on(Connection.Connected, function() {
                var session = new Session(conn);
                session.sessionSM.bind(recordSessionTransitions);
                session.begin({ nextOutgoingId: 1, incomingWindow: 100, outgoingWindow: 100 });
            });
            conn.open('amqp://localhost:'+server.port);
            server.assertSequence(function() {
                conn.close();
                assertTransitions(transitions, [ 'DISCONNECTED', 'START', 'HDR_SENT', 'HDR_EXCH', 'OPEN_SENT', 'OPENED', 'CLOSE_RCVD', 'DISCONNECTED' ]);
                assertTransitions(sessionTransitions, [ 'UNMAPPED', 'BEGIN_SENT', 'MAPPED', 'END_RCVD', 'UNMAPPED' ]);
                done();
            });
        });
/*
        it('should emit events', function(done) {
            server = new MockServer();
            server.setSequence([ constants.amqpVersion, openBuf(), closeBuf() ], [ constants.amqpVersion, openBuf(), [ true, closeBuf(new AMQPError(AMQPError.ConnectionForced, 'test')) ] ]);
            var conn = new Connection({ containerId: 'test', hostname: 'localhost' });
            server.setup(conn);
            var transitions = [];
            var recordTransitions = function(evt, oldS, newS) { transitions.push(oldS+'=>'+newS); };
            var events = [];
            conn.on(Connection.Connected, function() { events.push(Connection.Connected); });
            conn.on(Connection.Disconnected, function() { events.push(Connection.Disconnected); });
            conn.on(Connection.FrameReceived, function(frame) { events.push([ Connection.FrameReceived, frame ]); });
            conn.on(Connection.ErrorReceived, function(err) { events.push([ Connection.ErrorReceived, err ]); });
            conn.connSM.bind(recordTransitions);
            conn.open('amqp://localhost:'+server.port);
            server.assertSequence(function() {
                conn.close();
                assertTransitions(transitions, [ 'DISCONNECTED', 'START', 'HDR_SENT', 'HDR_EXCH', 'OPEN_SENT', 'OPENED', 'CLOSE_RCVD', 'DISCONNECTED' ]);
                events.length.should.eql(3);
                events[0].should.eql(Connection.Connected);
                events[1].should.eql(Connection.Disconnected);
                events[2][0].should.eql(Connection.ErrorReceived);
                done();
            });
        });*/
    });
});
<commit_msg>Add test for session events<commit_after>var debug       = require('debug')('amqp10-test_connection'),
    should      = require('should'),
    builder     = require('buffer-builder'),

    constants   = require('../lib/constants'),

    Connection  = require('../lib/connection'),
    Session     = require('../lib/session'),

    AMQPError   = require('../lib/types/amqp_error'),

    AttachFrame = require('../lib/frames/attach_frame'),
    BeginFrame  = require('../lib/frames/begin_frame'),
    CloseFrame  = require('../lib/frames/close_frame'),
    EndFrame    = require('../lib/frames/end_frame'),
    OpenFrame   = require('../lib/frames/open_frame'),

    MockServer  = require('./mock_amqp');

function openBuf(options) {
    var open = new OpenFrame(options || { containerId: 'test', hostname: 'localhost' });
    return open.outgoing();
}

function beginBuf(options) {
    var opts = { nextOutgoingId: 1, incomingWindow: 100, outgoingWindow: 100 };
    for (var prop in options) { opts[prop] = options[prop]; }
    var begin = new BeginFrame(opts);
    return begin.outgoing();
}

function endBuf(err) {
    var end = new EndFrame(err);
    return end.outgoing();
}

function closeBuf(err) {
    var close = new CloseFrame(err);
    return close.outgoing();
}

describe('Session', function() {
    var assertTransitions = function(actual, expected) {
        actual.length.should.eql(expected.length-1, "Wrong number of state transitions: Actual " + JSON.stringify(actual) + " vs. Expected " + JSON.stringify(expected));
        for (var idx = 0; idx < expected.length - 1; ++idx) {
            var curTransition = expected[idx] + '=>' + expected[idx+1];
            actual[idx].should.eql(curTransition, "Wrong transition at step "+idx);
        }
    };

    describe('#begin()', function() {
        // NOTE: Only works if you have a local AMQP server running
        /*
         it('should connect to activemq', function(done) {
         this.timeout(0);
         var conn = new Connection({ containerId: 'test', hostname: 'localhost' });
         conn.open('amqp://localhost/');
         setTimeout(function() {
         conn.close();
         done();
         }, 5000);
         });
         */
        var server = null;

        afterEach(function (done) {
            if (server) {
                server.teardown();
                server = null;
            }
            done();
        });

        it('should go through begin/end cycle as asked', function(done) {
            server = new MockServer();
            server.setSequence([ constants.amqpVersion, openBuf(), beginBuf(), endBuf(), closeBuf() ],
                [ constants.amqpVersion, openBuf(), beginBuf({ remoteChannel: 5 }), [ true, endBuf(new AMQPError(AMQPError.ConnectionForced, 'test')) ], [ true, closeBuf()] ]);
            var conn = new Connection({ containerId: 'test', hostname: 'localhost' });
            server.setup(conn);
            var transitions = [];
            var sessionTransitions = [];
            var recordTransitions = function(evt, oldS, newS) { transitions.push(oldS+'=>'+newS); };
            var recordSessionTransitions = function(evt, oldS, newS) { sessionTransitions.push(oldS+'=>'+newS); };
            conn.connSM.bind(recordTransitions);
            conn.on(Connection.Connected, function() {
                var session = new Session(conn);
                session.sessionSM.bind(recordSessionTransitions);
                session.begin({ nextOutgoingId: 1, incomingWindow: 100, outgoingWindow: 100 });
            });
            conn.open('amqp://localhost:'+server.port);
            server.assertSequence(function() {
                conn.close();
                assertTransitions(transitions, [ 'DISCONNECTED', 'START', 'HDR_SENT', 'HDR_EXCH', 'OPEN_SENT', 'OPENED', 'CLOSE_RCVD', 'DISCONNECTED' ]);
                assertTransitions(sessionTransitions, [ 'UNMAPPED', 'BEGIN_SENT', 'MAPPED', 'END_RCVD', 'UNMAPPED' ]);
                done();
            });
        });

        it('should emit events', function(done) {
            server = new MockServer();
            server.setSequence([ constants.amqpVersion, openBuf(), beginBuf(), endBuf(), closeBuf() ],
                [ constants.amqpVersion, openBuf(), beginBuf({ remoteChannel: 5 }), [ true, endBuf(new AMQPError(AMQPError.ConnectionForced, 'test')) ], [ true, closeBuf()] ]);
            var conn = new Connection({ containerId: 'test', hostname: 'localhost' });
            server.setup(conn);
            var events = [];
            conn.on(Connection.Connected, function() {
                var session = new Session(conn);
                session.on(Session.Mapped, function() { events.push(Session.Mapped); });
                session.on(Session.ErrorReceived, function(err) { events.push([ Session.ErrorReceived, err ]); });
                session.on(Session.Unmapped, function() { events.push(Session.Unmapped); });
                session.begin({ nextOutgoingId: 1, incomingWindow: 100, outgoingWindow: 100 });
            });
            conn.open('amqp://localhost:'+server.port);
            server.assertSequence(function() {
                conn.close();
                events.length.should.eql(3);
                events[0].should.eql(Session.Mapped);
                events[1][0].should.eql(Session.ErrorReceived);
                events[2].should.eql(Session.Unmapped);
                done();
            });
        });
    });
});
<|endoftext|>


<commit_before>'use strict';

describe('$FB', function () {

  var MODULE = 'ezfb',
      APP_ID = 'some fb app id',
      DELAY = 999999999999;

  beforeEach(function () {
    module(MODULE);
  });

  describe('configuration and initialization', function () {

    var loadSDKSpy, initSpy;

    beforeEach(function () {
      loadSDKSpy = jasmine.createSpy('load sdk');
      initSpy = jasmine.createSpy('init');

      module(function ($provide) {
        $provide.decorator('$window', function ($delegate) {
          angular.extend($delegate, {
            FB: {
              init: initSpy
            }
          });
          return $delegate;
        });
      });
    });

    it('should try to load FB JS SDK once', function () {
      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction(loadSDKSpy);
        $FBProvider.setInitParams({
          appId: APP_ID
        });
      });
      inject(function ($FB) {
        expect(loadSDKSpy.callCount).toEqual(1);
      });

    });

    it('should load FB JS SDK with correct locale', function () {
      var LOCALE = 'zhTW';

      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction(['$fbLocale', function ($fbLocale) {
          loadSDKSpy($fbLocale);
        }]);
        $FBProvider.setLocale(LOCALE);
        $FBProvider.setInitParams({
          appId: APP_ID
        });
      });
      inject(function ($FB) {
        expect(loadSDKSpy.callCount).toEqual(1);
        expect(loadSDKSpy.mostRecentCall.args[0]).toEqual(LOCALE);
      });

    });

    it('should call init function with correct appId', function () {
      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction(['$fbAsyncInit', function ($fbAsyncInit) {
          // Can't rely on default load SDK function here
          $fbAsyncInit();
        }]);
        $FBProvider.setInitParams({
          appId: APP_ID
        });
      });
      inject(function ($FB, $rootScope) {
        $rootScope.$apply();
        expect(initSpy.callCount).toEqual(1);
        expect(initSpy.mostRecentCall.args[0]).toEqual({
          appId      : APP_ID,
          status     : true,
          cookie     : true,
          xfbml      : true
        });
      });
    });

    it('should call init function even sdk is loaded asynchronously', function () {
      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction([
                 '$fbAsyncInit', '$timeout',
        function ($fbAsyncInit,   $timeout) {
          // Delay a bit
          $timeout(function () {
            $fbAsyncInit();
          }, DELAY);
        }]);
        $FBProvider.setInitParams({
          appId: APP_ID
        });
      });
      inject(function ($FB, $rootScope, $timeout) {
        $rootScope.$apply();
        $timeout.flush();
        expect(initSpy.callCount).toEqual(1);
      });
    });

    it('should call init function when setting up parameters in the run phase', function () {
      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction(['$fbAsyncInit', function ($fbAsyncInit) {
          // Can't rely on default load SDK function here
          $fbAsyncInit();
        }]);
      });
      inject(function ($FB, $rootScope) {
        $FB.init({
          appId: APP_ID
        });
        $rootScope.$apply();
        expect(initSpy).toHaveBeenCalled();
      });
    });

  });

  describe('the instance', function () {
    var API_RESPONSE = {
      angular: 1,
      easyfb: 2
    };

    var fbMockCallSpy, fbMockCallbackSpy;

    var mockSDKApi = function (apiPath, value) {
      module(function ($provide) {
        var pathAssign = function (obj, pathStr, value) {
          var paths = pathStr.split(/\./);

          if (paths.length === 0) {
            return;
          }

          var path = paths.shift();

          if (paths.length === 0) {
            obj[path] = value;
            return;
          }

          if (!obj[path]) {
            obj[path] = {};
          }

          pathAssign(obj[path], paths.join('.'), value);
        };

        var mockFB = {
          // Still required to provide an `init` function
          init: angular.noop
        };
        pathAssign(mockFB, apiPath, value);

        $provide.decorator('$window', function ($delegate) {
          angular.extend($delegate, {
            FB: mockFB
          });
          return $delegate;
        });
      });
    };

    beforeEach(function () {
      fbMockCallSpy = jasmine.createSpy('fb api call');
      fbMockCallbackSpy = jasmine.createSpy('fb api callback');

      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction(['$fbAsyncInit', function ($fbAsyncInit) {
          // Can't rely on default load SDK function here
          $fbAsyncInit();
        }]);
      });
    });

    describe('.api', function () {
      // https://developers.facebook.com/docs/javascript/reference/FB.api/

      var $FB, $rootScope;

      beforeEach(function () {
        mockSDKApi('api', function () {
          var args = [].slice.call(arguments);

          fbMockCallSpy.apply(jasmine, args);

          if (typeof args[1] === 'function') {
            args[1](API_RESPONSE);
          }
          else if (typeof args[2] === 'function') {
            args[2](API_RESPONSE);
          }
          else if (typeof args[3] === 'function') {
            args[3](API_RESPONSE);
          }
        });

        inject(function (_$FB_, _$rootScope_) {
          $FB = _$FB_;
          $rootScope = _$rootScope_;
        });
      });

      it('should call FB.api', function () {
        $FB.init({
          appId: APP_ID
        });

        $FB.api('/me', angular.noop);
        $rootScope.$apply();
        expect(fbMockCallSpy.callCount).toEqual(1);
      });

      it('should call FB.api after FB.init', function () {
        inject(function ($timeout) {
          $FB.api('/me', angular.noop);

          $timeout(function () {
            $FB.init({
              appId: APP_ID
            });
          }, DELAY)

          $timeout.flush();

          expect(fbMockCallSpy.callCount).toEqual(1);
        });
      });

      it('should trigger callbacks passed with different arg position with correct response', function () {
        $FB.init({
          appId: APP_ID
        });

        $FB.api('/me', fbMockCallbackSpy);
        $rootScope.$apply();
        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);

        $FB.api('/me', {fields: 'last_name'}, fbMockCallbackSpy);
        $rootScope.$apply();
        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);

        $FB.api('/me/feed', 'post', { message: 'post something' }, fbMockCallbackSpy);
        $rootScope.$apply();
        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);

        expect(fbMockCallbackSpy.callCount).toEqual(3);
      });
    });

  });

});
<commit_msg>Adds more test specs<commit_after>'use strict';

describe('$FB', function () {

  var MODULE = 'ezfb',
      APP_ID = 'some fb app id',
      DELAY = 999999999999,
      DEFAULT_INIT_PARAMS = {
        status     : true,
        cookie     : true,
        xfbml      : true
      };

  beforeEach(module(MODULE));

  describe('configuration and initialization', function () {

    var loadSDKSpy, initSpy;

    beforeEach(function () {
      loadSDKSpy = jasmine.createSpy('load sdk');
      initSpy = jasmine.createSpy('init');

      module(function ($provide) {
        $provide.decorator('$window', function ($delegate) {
          $delegate.FB = {
            init: initSpy
          };
          return $delegate;
        });
      });
    });

    it('should try to load FB JS SDK once', function () {
      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction(loadSDKSpy);
        $FBProvider.setInitParams({
          appId: APP_ID
        });
      });
      inject(function ($FB) {
        expect(loadSDKSpy.callCount).toEqual(1);
      });

    });

    it('should load FB JS SDK with correct locale', function () {
      var LOCALE = 'zhTW';

      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction(['$fbLocale', function ($fbLocale) {
          loadSDKSpy($fbLocale);
        }]);
        $FBProvider.setLocale(LOCALE);
        $FBProvider.setInitParams({
          appId: APP_ID
        });
      });
      inject(function ($FB) {
        expect(loadSDKSpy.callCount).toEqual(1);
        expect(loadSDKSpy.mostRecentCall.args[0]).toEqual(LOCALE);
      });

    });

    it('should call init function with correct appId', function () {
      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction(['$fbAsyncInit', function ($fbAsyncInit) {
          // Can't rely on default load SDK function here
          $fbAsyncInit();
        }]);
        $FBProvider.setInitParams({
          appId: APP_ID
        });
      });
      inject(function ($FB, $rootScope) {
        $rootScope.$apply();

        expect(initSpy.callCount).toEqual(1);
        expect(initSpy.mostRecentCall.args[0]).toEqual(
          angular.extend(DEFAULT_INIT_PARAMS, {
            appId: APP_ID
          })
        );
      });
    });

    it('should call init function even sdk is loaded asynchronously', function () {
      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction([
                 '$fbAsyncInit', '$timeout',
        function ($fbAsyncInit,   $timeout) {
          // Delay a bit
          $timeout(function () {
            $fbAsyncInit();
          }, DELAY);
        }]);
        $FBProvider.setInitParams({
          appId: APP_ID
        });
      });
      inject(function ($FB, $rootScope, $timeout) {
        $rootScope.$apply();
        $timeout.flush();

        expect(initSpy.callCount).toEqual(1);
      });
    });

    it('should call init function when setting up parameters in the run phase', function () {
      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction(['$fbAsyncInit', function ($fbAsyncInit) {
          // Can't rely on default load SDK function here
          $fbAsyncInit();
        }]);
      });
      inject(function ($FB, $rootScope) {
        $FB.init({
          appId: APP_ID
        });
        $rootScope.$apply();

        expect(initSpy).toHaveBeenCalled();
      });
    });

  });

  describe('the instance', function () {
    var API_RESPONSE = {
      angular: 1,
      easyfb: 2
    };

    var fbMockCallSpy, fbMockCallbackSpy, fbMockPromiseSpy;

    var mockSDKApi = function (apiPath, value) {
      module(function ($provide) {
        var pathAssign = function (obj, pathStr, value) {
          var paths = pathStr.split(/\./);

          if (paths.length === 0) {
            return;
          }

          var path = paths.shift();

          if (paths.length === 0) {
            obj[path] = value;
            return;
          }

          if (!obj[path]) {
            obj[path] = {};
          }

          pathAssign(obj[path], paths.join('.'), value);
        };

        if (typeof apiPath === 'string') {
          apiPath = [apiPath];
        }

        var mockFB = {
          // Still required to provide an `init` function
          init: angular.noop
        };
        angular.forEach(apiPath, function (p) {
          pathAssign(mockFB, p, value);
        });

        $provide.decorator('$window', function ($delegate) {
          $delegate.FB = mockFB;
          return $delegate;
        });
      });
    };

    beforeEach(function () {
      fbMockCallSpy = jasmine.createSpy('fb api call');
      fbMockCallbackSpy = jasmine.createSpy('fb api callback');
      fbMockPromiseSpy = jasmine.createSpy('fb api promise');

      module(function ($FBProvider) {
        $FBProvider.setLoadSDKFunction(['$fbAsyncInit', function ($fbAsyncInit) {
          // Can't rely on default load SDK function here
          $fbAsyncInit();
        }]);
      });
    });

    describe('.init', function () {
      /**
       * Ref: https://developers.facebook.com/docs/javascript/reference/FB.init
       */

      var $FB, $rootScope;

      beforeEach(function () {
        mockSDKApi('init', function () {
          var args = [].slice.call(arguments);

          fbMockCallSpy.apply(jasmine, args);
        });

        inject(function (_$FB_, _$rootScope_) {
          $FB = _$FB_;
          $rootScope = _$rootScope_;
        });
      });

      it('should call FB.init with correct parameters and set $FB.$$ready to `true`', function () {
        $FB.init({
          appId: APP_ID
        });

        $rootScope.$apply();

        expect(fbMockCallSpy.callCount).toEqual(1);
        expect(fbMockCallSpy.mostRecentCall.args[0]).toEqual(
          angular.extend(DEFAULT_INIT_PARAMS, {
            appId: APP_ID
          })
        );
        expect($FB.$$ready).toBeTruthy();
      });

    });

    describe('.api', function () {
      /**
       * Ref: https://developers.facebook.com/docs/javascript/reference/FB.api
       */

      var $FB, $rootScope;

      beforeEach(function () {
        mockSDKApi('api', function () {
          var args = [].slice.call(arguments);

          fbMockCallSpy.apply(jasmine, args);

          if (typeof args[1] === 'function') {
            args[1](API_RESPONSE);
          }
          else if (typeof args[2] === 'function') {
            args[2](API_RESPONSE);
          }
          else if (typeof args[3] === 'function') {
            args[3](API_RESPONSE);
          }
        });

        inject(function (_$FB_, _$rootScope_) {
          $FB = _$FB_;
          $rootScope = _$rootScope_;
        });
      });

      it('should call FB.api', function () {
        $FB.init({
          appId: APP_ID
        });

        $FB.api('/me');
        $FB.api('/me', null);
        $FB.api('/me', angular.noop);
        $rootScope.$apply();

        expect(fbMockCallSpy.callCount).toEqual(3);
      });

      it('should call FB.api after FB.init is called', function () {
        inject(function ($timeout) {
          $FB.api('/me');

          $timeout(function () {
            expect(fbMockCallSpy.callCount).toEqual(0);

            $FB.init({
              appId: APP_ID
            });
          }, DELAY)

          $timeout.flush();

          expect(fbMockCallSpy.callCount).toEqual(1);
        });
      });

      it('should trigger callbacks under different arguments situation with correct response', function () {
        $FB.init({
          appId: APP_ID
        });

        $FB.api('/me', fbMockCallbackSpy);
        $rootScope.$apply();

        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);

        $FB.api('/me', {fields: 'last_name'}, fbMockCallbackSpy);
        $rootScope.$apply();

        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);

        $FB.api('/me/feed', 'post', { message: 'post something' }, fbMockCallbackSpy);
        $rootScope.$apply();

        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);

        expect(fbMockCallbackSpy.callCount).toEqual(3);
      });

      it('should trigger promise under different arguments situation with correct response', function () {
        $FB.init({
          appId: APP_ID
        });

        $FB.api('/me').then(fbMockPromiseSpy);
        $rootScope.$apply();

        expect(fbMockPromiseSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);

        $FB.api('/me', {fields: 'last_name'}).then(fbMockPromiseSpy);
        $rootScope.$apply();

        expect(fbMockPromiseSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);

        $FB.api('/me/feed', 'post', { message: 'post something' }).then(fbMockPromiseSpy);
        $rootScope.$apply();

        expect(fbMockPromiseSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);

        expect(fbMockPromiseSpy.callCount).toEqual(3);
      });

      it('should trigger both callback and promise with correct response', function () {
        $FB.init({
          appId: APP_ID
        });

        $FB.api('/me', fbMockCallbackSpy).then(fbMockPromiseSpy);
        $rootScope.$apply();

        expect(fbMockCallbackSpy.callCount).toEqual(1);
        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);
        expect(fbMockPromiseSpy.callCount).toEqual(1);
        expect(fbMockPromiseSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);
      });
    });

    describe('.ui', function () {
      /**
       * Ref: https://developers.facebook.com/docs/javascript/reference/FB.ui
       */

      var $FB, $rootScope;

      var UI_PARAMS = {war: 1, machine: 2, rox: 3};

      beforeEach(function () {
        mockSDKApi('ui', function () {
          var args = [].slice.call(arguments);

          fbMockCallSpy.apply(jasmine, args);

          if (typeof args[1] === 'function') {
            args[1](API_RESPONSE);
          }
        });

        inject(function (_$FB_, _$rootScope_) {
          $FB = _$FB_;
          $rootScope = _$rootScope_;
        });

        $FB.init({
          appId: APP_ID
        });
      });

      it('should call FB.ui', function () {
        $FB.ui(UI_PARAMS);
        $FB.ui(UI_PARAMS, null);
        $FB.ui(UI_PARAMS, angular.noop);
        $rootScope.$apply();

        expect(fbMockCallSpy.callCount).toEqual(3);
      });

      it('should trigger callback with correct response', function () {
        $FB.ui(UI_PARAMS, fbMockCallbackSpy);
        $rootScope.$apply();

        expect(fbMockCallbackSpy.callCount).toEqual(1);
        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);
      });

      it('should trigger promise with correct response ', function () {
        $FB.ui(UI_PARAMS).then(fbMockPromiseSpy);
        $rootScope.$apply();

        expect(fbMockPromiseSpy.callCount).toEqual(1);
        expect(fbMockPromiseSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);
      });

      it('should trigger both callback and promise with correct response', function () {
        $FB.ui(UI_PARAMS, fbMockCallbackSpy).then(fbMockPromiseSpy);
        $rootScope.$apply();

        expect(fbMockCallbackSpy.callCount).toEqual(1);
        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);
        expect(fbMockPromiseSpy.callCount).toEqual(1);
        expect(fbMockPromiseSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);
      });
    });

    describe('.getAuthResponse', function () {
      /**
       * Ref: https://developers.facebook.com/docs/reference/javascript/FB.getAuthResponse
       */

      var $FB, $rootScope;

      beforeEach(function () {
        mockSDKApi('getAuthResponse', function () {
          var args = [].slice.call(arguments);

          fbMockCallSpy.apply(jasmine, args);

          return API_RESPONSE;
        });

        inject(function (_$FB_, _$rootScope_) {
          $FB = _$FB_;
          $rootScope = _$rootScope_;
        });

        $FB.init({
          appId: APP_ID
        });
      });

      it('should call FB.getAuthResponse', function () {
        $FB.getAuthResponse();

        expect(fbMockCallSpy.callCount).toEqual(1);
      });

      it('should retrieve synchronous response', function () {
        expect($FB.getAuthResponse()).toEqual(API_RESPONSE);
      });
    });

    /**
     * Ref:
     *   https://developers.facebook.com/docs/reference/javascript/FB.getLoginStatus
     *   https://developers.facebook.com/docs/reference/javascript/FB.login
     *   https://developers.facebook.com/docs/reference/javascript/FB.logout
     */
    angular.forEach([
      'getLoginStatus', 'login', 'logout'
    ], function (apiName) {

      describe('.' + apiName, function () {

        var $FB, $rootScope;

        beforeEach(function () {
          mockSDKApi(apiName, function () {
            var args = [].slice.call(arguments);

            fbMockCallSpy.apply(jasmine, args);

            if (typeof args[0] === 'function') {
              args[0]({
                res: apiName
              });
            }
          });

          inject(function (_$FB_, _$rootScope_) {
            $FB = _$FB_;
            $rootScope = _$rootScope_;
          });

          $FB.init({
            appId: APP_ID
          });
        });

        it('should call FB.' + apiName, function () {
          $FB[apiName]();
          $rootScope.$apply();

          expect(fbMockCallSpy.callCount).toEqual(1);
        });

        it('should trigger callback with correct response', function () {
          $FB[apiName](fbMockCallbackSpy);
          $rootScope.$apply();

          expect(fbMockCallbackSpy.callCount).toEqual(1);
          expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual({
            res: apiName
          });
        });

        it('should trigger promise with correct response', function () {
          $FB[apiName]().then(fbMockPromiseSpy);
          $rootScope.$apply();

          expect(fbMockPromiseSpy.callCount).toEqual(1);
          expect(fbMockPromiseSpy.mostRecentCall.args[0]).toEqual({
            res: apiName
          });
        });

        it('should trigger both callback and promise with correct response', function () {
          $FB[apiName](fbMockCallbackSpy).then(fbMockPromiseSpy);
          $rootScope.$apply();

          expect(fbMockCallbackSpy.callCount).toEqual(1);
          expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual({
            res: apiName
          });
          expect(fbMockPromiseSpy.callCount).toEqual(1);
          expect(fbMockPromiseSpy.mostRecentCall.args[0]).toEqual({
            res: apiName
          });
        });
      });

    });

    describe('.XFBML.parse', function () {
      var $FB, $rootScope, elm;

      beforeEach(function () {
        mockSDKApi('XFBML.parse', function () {
          var args = [].slice.call(arguments);

          fbMockCallSpy.apply(jasmine, args);

          if (typeof args[1] === 'function') {
            args[1](API_RESPONSE);
          }
        });

        inject(function (_$FB_, _$rootScope_) {
          $FB = _$FB_;
          $rootScope = _$rootScope_;
        });

        $FB.init({
          appId: APP_ID
        });

        elm = angular.element('<div>')[0];
      });

      it('should call FB.XFBML.parse', function () {
        $FB.XFBML.parse();
        $rootScope.$apply();

        expect(fbMockCallSpy.callCount).toEqual(1);

        $FB.XFBML.parse(elm);
        $rootScope.$apply();

        expect(fbMockCallSpy.callCount).toEqual(2);
      });

      it('should trigger callback with correct response', function () {
        $FB.XFBML.parse(elm, fbMockCallbackSpy);
        $rootScope.$apply();

        expect(fbMockCallbackSpy.callCount).toEqual(1);
        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);
      });

      it('should trigger promise with correct response', function () {
        $FB.XFBML.parse(elm).then(fbMockPromiseSpy);
        $rootScope.$apply();

        expect(fbMockPromiseSpy.callCount).toEqual(1);
        expect(fbMockPromiseSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);
      });

      it('should trigger both callback and promise with correct response', function () {
        $FB.XFBML.parse(elm, fbMockCallbackSpy).then(fbMockPromiseSpy);
        $rootScope.$apply();

        expect(fbMockCallbackSpy.callCount).toEqual(1);
        expect(fbMockCallbackSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);
        expect(fbMockPromiseSpy.callCount).toEqual(1);
        expect(fbMockPromiseSpy.mostRecentCall.args[0]).toEqual(API_RESPONSE);
      });
    });
  });

});
<|endoftext|>


<commit_before>var Command = require('../../lib/command');

describe('Command', function () {
  describe('constructor()', function () {
    it('should reject when the command isnt a string', function () {
      var command = new Command(123);
      return command.promise.catch(function (err) {
        expect(err).to.be.instanceof(Error);
      });
    });

    it('should flatten the args', function () {
      var command = new Command('get', ['foo', ['bar', ['zoo']]]);
      expect(command.args).to.eql(['foo', 'bar', ['zoo']]);
    });
  });

  describe('#toWritable()', function () {
    it('should return correct string', function () {
      var command = new Command('get', ['foo', 'bar', 'zooo']);
      expect(command.toWritable()).to.eql('*4\r\n$3\r\nget\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$4\r\nzooo\r\n');
    });

    it('should return buffer when there\'s at least one arg is a buffer', function () {
      var command = new Command('get', ['foo', new Buffer('bar'), 'zooo']);
      var result = command.toWritable();
      expect(result).to.be.instanceof(Buffer);
      expect(result.toString()).to.eql('*4\r\n$3\r\nget\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$4\r\nzooo\r\n');
    });
  });
});
<commit_msg>Add tests for encode converting<commit_after>var Command = require('../../lib/command');

describe('Command', function () {
  describe('constructor()', function () {
    it('should reject when the command isnt a string', function () {
      var command = new Command(123);
      return command.promise.catch(function (err) {
        expect(err).to.be.instanceof(Error);
      });
    });

    it('should flatten the args', function () {
      var command = new Command('get', ['foo', ['bar', ['zoo']]]);
      expect(command.args).to.eql(['foo', 'bar', ['zoo']]);
    });
  });

  describe('#toWritable()', function () {
    it('should return correct string', function () {
      var command = new Command('get', ['foo', 'bar', 'zooo']);
      expect(command.toWritable()).to.eql('*4\r\n$3\r\nget\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$4\r\nzooo\r\n');
    });

    it('should return buffer when there\'s at least one arg is a buffer', function () {
      var command = new Command('get', ['foo', new Buffer('bar'), 'zooo']);
      var result = command.toWritable();
      expect(result).to.be.instanceof(Buffer);
      expect(result.toString()).to.eql('*4\r\n$3\r\nget\r\n$3\r\nfoo\r\n$3\r\nbar\r\n$4\r\nzooo\r\n');
    });
  });

  describe('#resolve()', function () {
    it('should return buffer when replyEncoding is not set', function (done) {
      var command = new Command('get', ['foo'], null, function (err, result) {
        expect(result).to.be.instanceof(Buffer);
        expect(result.toString()).to.eql('foo');
        done();
      });
      command.resolve(new Buffer('foo'));
    });

    it('should covert result to string if replyEncoding is specified', function (done) {
      var command = new Command('get', ['foo'], 'utf8', function (err, result) {
        expect(result).to.eql('foo');
        done();
      });
      command.resolve(new Buffer('foo'));
    });

    it('should regard replyEncoding', function (done) {
      var base64 = new Buffer('foo').toString('base64');
      var command = new Command('get', ['foo'], 'base64', function (err, result) {
        expect(result).to.eql(base64);
        done();
      });
      command.resolve(new Buffer('foo'));
    });
  });
});
<|endoftext|>