<commit_before>'use strict'

// A Block is an svg group that do any behavior, this can contain Fields or other Blocks.
//  This is an abstract Block class, don't instance this.
//  All block prototypes live in blocks folder or create your own using the Block API
//  Block can be instantiable from workspaces with addBlock method, see the basic-demo
// TODOs:
//  - delete a block with remove method and delete key when selected (methods and animation).
//  - documentation for Block API

BB.Block = BB.Component.prototype.create({
  constructor: function(name, options, customOptions)  {
    BB.Component.prototype.constructor.call(this, 'Block');
    this.name = name;
    this.id = null;
    this.x = 0;
    this.y = 0;
    this.width = 20;
    this.height = 20;
    this.container = null; // contains attached elements(border) and SVG document
    this.childContainer = null; // svg group that contains all children
    this.statement = true;
    this.root = null;
    this.nested = true; // Blocks are nested by default, allows toTopPropagate
    this.fields = [];
    this.attachDraggable = [];
    this.style = {
      className: 'BBComponentBlock'
    };
    this.selectedClass = 'BBComponentBlockSelected';
    // Capabilities of block
    this.selectable = true;
    this.draggable_ = true;

    this.metrics = {
      borderRadius: 2,
      borderWidth: 1,
      initialSpace: {x: 4, y: 0},
      finalSpace: {x: 4, y: 4},
      fieldSpace: 5,
      topRowSpace: 2.5,
      bottomRowSpace: 2.5,
      widthType: 'globalWidth',
    };

    // Options
    if (customOptions) { //options of a custom blocks
      this.customOptions = customOptions;
    }

    if (!options) {
      return;
    }
    // TODO: Implement validation for options
    this.optionList = ['x',
                       'y',
                       'stylingFunction',
                       'colorPalette',
                       'metrics'];
    for (var i = 0,el; el = this.optionList[i]; i++) {
      if (options.hasOwnProperty(el)) {
        this[el] = options[el];
      }
    }
    if (options.hasOwnProperty('selectable')) {
      this.selectable = options.selectable;
    }
    if (options.hasOwnProperty('draggable')) {
      this.draggable_ = options.draggable;
    }
    if (options.hasOwnProperty('selected')) {
      this.selected_ = options.selected;
    }
    if (options.render) {
      this.render();
    }
  },

  appendField: function(field) {
    field.index = this.fields.length;
    this.fields.push(field);
    return field;
  },

  newRow: function() {
      this.fields.push('newRow');
  },

  widthType: function(type) {
    if (type == 'grouped') {
      this.fields.push('groupedWidth');
    } else if (type == 'global') {
      this.fields.push('globalWidth');
    } else if (type == 'single') {
      this.fields.push('singleWidth');
    } else {
      throw 'Unknown width type';
    }
  },

  render: function() {
    if (!this.workspace) {
      throw 'Blocks must have a workspace to be rendered';
      return;
    }
    if (!this.rendered_) {
      if (!this.colorPalette) {
        this.colorPalette = BB.colorPalettes.block.light; //default palette
      }
      // styling
      this.bgColor = this.colorPalette.background;
      this.borderColor = this.colorPalette.border;
      if (this.stylingFunction) {
        this.stylingFunction();
      }
      if (!this.initialized_) {
        this.init(this.customOptions); // attributes of custom Block
        this.initialized_ = true;
      } else {
        this.root.remove();
      }
      //needs create container before initSVG, because it render the fields
      this.container = this.workspace.root.group();
      this.container.move(this.x, this.y);
      this.initSvg(); // compute graphics for rendering
      //render block - render fields and all block svg
      this.renderBlock_();
      // render children
      this.childContainer = this.workspace.root.group();
      for (var i = 0; i < this.children.length; i++) {
        this.children[i].render();
        this.childContainer.add(this.children[i].container);
      }
      this.container.add(this.root);
      this.container.add(this.childContainer);
      // add fields to top of container
      for (var i = 0; i < this.fields.length; i++) {
        if (typeof(this.fields[i]) == 'object') {
          this.fields[i].toTop();
        }
      }
      this.attachDraggable.push(this.root);
      this.updateDraggable();
      this.workspace.childContainer.add(this.container);
      this.workspace.childRendered(this);
      if (this.attach) {
        this.attach(); // attach additional svg elements to block
      }
      if (this.selected_) {
        this.root.addClass(this.selectedClass);
      } else {
        this.root.removeClass(this.selectedClass);
      }
      this.rendered_ = true;
    }
  },

  attachEvents: function(child) {
    this.attachDraggable.push(child);
    this.container.draggable(this.workspace, null, this.attachDraggable);
  },

  updateDraggable: function() {
    // Prevents text selection and default behavior
    var this_ = this;
    if (this.toTopClosure == undefined) {
      this.pereventDefaultClosure = function(e) {
        e.preventDefault();
        e.stopPropagation();
      };
    }
    BB.attachToEls(this.attachDraggable, 'down', this.pereventDefaultClosure);
    if (this.draggable_) {
      this.container.draggable(this, null, this.attachDraggable);
    } else if (this.rendered_) {
      this.container.fixedDrag(); // Remove dragging from container
    }
    if (this.toTopClosure == undefined) {
      this.toTopClosure = function(e) {
        //console.log(this_.name + ' down');
        this_.setSelected(true);
        e.preventDefault();
        e.stopPropagation();
      };
    }
    BB.attachToEls(this.attachDraggable, 'down', this.toTopClosure);
  },

  dragstart: function() {
    this.root.addClass('BBComponentBlockDragging');
  },

  dragend: function() {
    this.root.removeClass('BBComponentBlockDragging');
  },

  setDraggable: function(bool) {
    this.draggable_ = bool;
    if (this.rendered_) {
      this.updateDraggable();
    }
  },

  // calculate metrics ,render fields if not are rendered or if rendered adds
  initSvg: function() {
    var globalWidth = this.metrics.initialSpace.x, box, maxHeight = 0;
    var metrics = {
      width: this.metrics.initialSpace.x,
      y: this.metrics.initialSpace.y
    };
    this.metrics.rows = [];
    this.metrics.numRows = 0;
    for (var i = 0; i < this.fields.length; i++) {
      if (typeof(this.fields[i]) == 'string') { //field control commands
        switch (this.fields[i]) {
            case 'newRow':
              metrics.y += maxHeight + this.metrics.bottomRowSpace + this.metrics.topRowSpace;
              metrics.width  += this.metrics.finalSpace.x - this.metrics.fieldSpace;
              this.metrics.rows[this.metrics.numRows] = {
                width: metrics.width,
                y: metrics.y,
                height: maxHeight + this.metrics.topRowSpace,
                widthType: this.metrics.widthType
              };
              this.metrics.numRows++;
              globalWidth = Math.max(metrics.width, globalWidth);
              metrics.width = this.metrics.initialSpace.x;
              maxHeight = 0;
              break;
            case 'globalWidth':
              this.metrics.widthType = 'globalWidth';
              break;
            case 'groupedWidth':
              this.metrics.widthType = 'groupedWidth';
              break;
            case 'singleWidth':
              this.metrics.widthType = 'singleWidth';
              break;
            default:
            throw 'Unknown width type';
        }
      } else {
        // updates incrementally metrics for dinamic fields rendering
        this.metrics.rows[this.metrics.numRows] = {
          width: metrics.width,
          y: metrics.y,
          height: maxHeight + this.metrics.topRowSpace,
          widthType: this.metrics.widthType
        };
        this.fields[i].row = this.metrics.numRows;
        if (this.fields[i].rendered_) {
          this.container.add(this.fields[i].container);
        } else {
          this.fields[i].render();
        }
        box = this.fields[i].bbox();
        this.fields[i].container.move(metrics.width, metrics.y + this.metrics.topRowSpace); // position of field
        metrics.width += box.width + this.metrics.fieldSpace;
        maxHeight = Math.max(maxHeight, box.height);
      }
    }
    metrics.width += this.metrics.finalSpace.x - this.metrics.fieldSpace;
    globalWidth = Math.max(metrics.width, globalWidth);
    metrics.y += box.height + this.metrics.finalSpace.y;
    this.metrics.rows.push({
      width: metrics.width,
      y: metrics.y,
      height: maxHeight,
      widthType: this.metrics.widthType
    });
    this.width = globalWidth;
    this.height = metrics.y;
    // compute widths
    var i, j, groupFound = false, maxWidth;
    for (i = 0; i < this.metrics.rows.length; i++) {
      if (this.metrics.rows[i].widthType == 'groupedWidth') {
        if (groupFound == false) {
          maxWidth = this.metrics.rows[i].width;
          for (j=i+1;j<this.metrics.rows.length && this.metrics.rows[j].widthType == 'groupedWidth'; j++) {
            maxWidth = Math.max(maxWidth, this.metrics.rows[j].width);
          }
          groupFound = true;
        }
        this.metrics.rows[i].width = maxWidth; // uniforms width
      } else {
         if (this.metrics.rows[i].widthType == 'globalWidth') {
           this.metrics.rows[i].width = this.width; // global width
         }
         groupFound = false;
      }
    }
    // compute heights
    var height, lastWidth = this.metrics.rows[0].width;
    this.metrics.rows[0].lastRadius = 'plane';
    for (i = 1; i < this.metrics.rows.length; i++) {
      height = this.metrics.rows[i].height;
      if (lastWidth < this.metrics.rows[i].width) {
        this.metrics.rows[i].lastRadius = 'convex';
        this.metrics.rows[i-1].nextRadius = 'concave';
      }else if (lastWidth > this.metrics.rows[i].width) {
        this.metrics.rows[i].lastRadius = 'concave';
        this.metrics.rows[i-1].nextRadius = 'convex';
      } else {
        this.metrics.rows[i].lastRadius = 'plane';
        this.metrics.rows[i-1].nextRadius = 'plane';
      }
      lastWidth = this.metrics.rows[i].width;
    }
    this.metrics.rows[this.metrics.rows.length-1].nextRadius = 'convex';
  },

  //renders the block root
  renderBlock_: function() {
    //TODO: block root must be a group
    var radius = this.metrics.borderRadius; // for typing
    var rowSpace = this.metrics.rowSpace; // for typing
    if (!this.root) {
      this.root = this.container.path();
    } else {
      this.root.clear(); // if rendered clear and update segments
    }
    this.root.M({x: radius, y: this.height})
                .q({x: -radius, y: 0}, {x: -radius, y: -radius})
                .v(-this.height + 2*radius) // left side
                .q({x: 0, y: -radius}, {x: radius, y: -radius});
    // top
    var row = this.metrics.rows[0]; //for typing
    var height = row.y;
    height -= radius;
    //first row
    this.root.H(row.width - radius)
              .q({x: radius, y: 0}, {x: radius, y: radius});
    //render by row rigth line - middle
    for (var i = 1; i < this.metrics.rows.length ; i++) {
      row = this.metrics.rows[i];
      height = row.y;
      height -= radius;
      if (row.lastRadius == 'convex') {
        this.root.q({x: 0, y: radius}, {x: radius, y: radius})
                 .H(row.width - radius)
                 .q({x: radius, y: 0}, {x: radius, y: radius})
                 .V(height);
      } else if (row.lastRadius == 'concave') {
        this.root.q({x: 0, y: radius}, {x: -radius, y: radius})
                 .H(row.width + radius)
                 .q({x: -radius, y: 0}, {x: -radius, y: radius})
                 .V(height);
      } else if (row.lastRadius == 'plane') {
        this.root.V(height);
      } else {
        throw 'Unsopported type of radius: ' + row.lastRadius;
      }
    }
    var finalRow = this.metrics.rows[this.metrics.rows.length - 1]; //for typing
    var finalWidth = finalRow.width;
    // bottom line
    this.root.q({x: 0, y: radius}, {x: -radius, y: radius})
             .h(-finalWidth + 2*radius)
             // TODO: report border bug in svg, when drag a block with black border, this project dont use that, use svg rect
    // this bug is resolved in chromium 44, TODO: test in chrome 42
             /*.stroke({ color: this.borderColor,
                       opacity: 1,
                       width: this.metrics.borderWidth
              }).fill(this.bgColor);*/
    this.root.fill(this.bgColor);
    this.root.addClass(this.style.className);
  },

  setColor: function(color) {
    this.bgColor = color;
    if (this.rendered_) {
      this.root.fill(color);
    }
  },

  fieldChanged: function(index) {
    // Redraw path
    this.initSvg();
    this.renderBlock_();
    /*if (this.fields[index].setSelected) {
      var tempSelected = this.fields[index].selected_;
      this.fields[index].setSelected(!tempSelected);
      this.fields[index].setSelected(tempSelected);
    }*/
  },

  onBlur: function() { // Unselect all fields
    var i, len = this.fields.length;
    for (i = 0; i < len; i++) {
      if (typeof(this.fields[i]) == 'object' && this.fields[i].setSelected) {
        this.fields[i].setSelected(false);
      }
    }
  },
  onUnRender: function() {
    var i, len = this.fields.length;
    for (i = 0; i < len; i++) {
      if (this.fields[i].unRender) {
        this.fields[i].unRender();
      }
    }
  }
});
<commit_msg>Block fieds layout fixed<commit_after>'use strict'

// A Block is an svg group that do any behavior, this can contain Fields or other Blocks.
//  This is an abstract Block class, don't instance this.
//  All block prototypes live in blocks folder or create your own using the Block API
//  Block can be instantiable from workspaces with addBlock method, see the basic-demo
// TODOs:
//  - delete a block with remove method and delete key when selected (methods and animation).
//  - documentation for Block API

BB.Block = BB.Component.prototype.create({
  constructor: function(name, options, customOptions)  {
    BB.Component.prototype.constructor.call(this, 'Block');
    this.name = name;
    this.id = null;
    this.x = 0;
    this.y = 0;
    this.width = 20;
    this.height = 20;
    this.container = null; // contains attached elements(border) and SVG document
    this.childContainer = null; // svg group that contains all children
    this.statement = true;
    this.root = null;
    this.nested = true; // Blocks are nested by default, allows toTopPropagate
    this.fields = [];
    this.attachDraggable = [];
    this.style = {
      className: 'BBComponentBlock'
    };
    this.selectedClass = 'BBComponentBlockSelected';
    // Capabilities of block
    this.selectable = true;
    this.draggable_ = true;

    this.metrics = {
      borderRadius: 2,
      borderWidth: 1,
      initialSpace: {x: 4, y: 4},
      finalSpace: {x: 4, y: 4},
      fieldSpace: 5,
      topRowSpace: 1.25,
      bottomRowSpace: 1.25,
      widthType: 'globalWidth',
    };

    // Options
    if (customOptions) { //options of a custom blocks
      this.customOptions = customOptions;
    }

    if (!options) {
      return;
    }
    // TODO: Implement validation for options
    this.optionList = ['x',
                       'y',
                       'stylingFunction',
                       'colorPalette',
                       'metrics'];
    for (var i = 0,el; el = this.optionList[i]; i++) {
      if (options.hasOwnProperty(el)) {
        this[el] = options[el];
      }
    }
    if (options.hasOwnProperty('selectable')) {
      this.selectable = options.selectable;
    }
    if (options.hasOwnProperty('draggable')) {
      this.draggable_ = options.draggable;
    }
    if (options.hasOwnProperty('selected')) {
      this.selected_ = options.selected;
    }
    if (options.render) {
      this.render();
    }
  },

  appendField: function(field) {
    field.index = this.fields.length;
    this.fields.push(field);
    return field;
  },

  newRow: function() {
      this.fields.push('newRow');
  },

  widthType: function(type) {
    if (type == 'grouped') {
      this.fields.push('groupedWidth');
    } else if (type == 'global') {
      this.fields.push('globalWidth');
    } else if (type == 'single') {
      this.fields.push('singleWidth');
    } else {
      throw 'Unknown width type';
    }
  },

  render: function() {
    if (!this.workspace) {
      throw 'Blocks must have a workspace to be rendered';
      return;
    }
    if (!this.rendered_) {
      if (!this.colorPalette) {
        this.colorPalette = BB.colorPalettes.block.light; //default palette
      }
      // styling
      this.bgColor = this.colorPalette.background;
      this.borderColor = this.colorPalette.border;
      if (this.stylingFunction) {
        this.stylingFunction();
      }
      if (!this.initialized_) {
        this.init(this.customOptions); // attributes of custom Block
        this.initialized_ = true;
      } else {
        this.root.remove();
      }
      //needs create container before initSVG, because it render the fields
      this.container = this.workspace.root.group();
      this.container.move(this.x, this.y);
      this.initSvg(); // compute graphics for rendering
      //render block - render fields and all block svg
      this.renderBlock_();
      // render children
      this.childContainer = this.workspace.root.group();
      for (var i = 0; i < this.children.length; i++) {
        this.children[i].render();
        this.childContainer.add(this.children[i].container);
      }
      this.container.add(this.root);
      this.container.add(this.childContainer);
      // add fields to top of container
      for (var i = 0; i < this.fields.length; i++) {
        if (typeof(this.fields[i]) == 'object') {
          this.fields[i].toTop();
        }
      }
      this.attachDraggable.push(this.root);
      this.updateDraggable();
      this.workspace.childContainer.add(this.container);
      this.workspace.childRendered(this);
      if (this.attach) {
        this.attach(); // attach additional svg elements to block
      }
      if (this.selected_) {
        this.root.addClass(this.selectedClass);
      } else {
        this.root.removeClass(this.selectedClass);
      }
      this.rendered_ = true;
    }
  },

  attachEvents: function(child) {
    this.attachDraggable.push(child);
    this.container.draggable(this.workspace, null, this.attachDraggable);
  },

  updateDraggable: function() {
    // Prevents text selection and default behavior
    var this_ = this;
    if (this.toTopClosure == undefined) {
      this.pereventDefaultClosure = function(e) {
        e.preventDefault();
        e.stopPropagation();
      };
    }
    BB.attachToEls(this.attachDraggable, 'down', this.pereventDefaultClosure);
    if (this.draggable_) {
      this.container.draggable(this, null, this.attachDraggable);
    } else if (this.rendered_) {
      this.container.fixedDrag(); // Remove dragging from container
    }
    if (this.toTopClosure == undefined) {
      this.toTopClosure = function(e) {
        //console.log(this_.name + ' down');
        this_.setSelected(true);
        e.preventDefault();
        e.stopPropagation();
      };
    }
    BB.attachToEls(this.attachDraggable, 'down', this.toTopClosure);
  },

  dragstart: function() {
    this.root.addClass('BBComponentBlockDragging');
  },

  dragend: function() {
    this.root.removeClass('BBComponentBlockDragging');
  },

  setDraggable: function(bool) {
    this.draggable_ = bool;
    if (this.rendered_) {
      this.updateDraggable();
    }
  },

  // calculate metrics ,render fields if not are rendered or if rendered adds
  initSvg: function() {
    var globalWidth = this.metrics.initialSpace.x, box, maxHeight = 0;
    var metrics = {
      width: this.metrics.initialSpace.x,
      y: this.metrics.initialSpace.y
    };
    this.metrics.rows = [];
    this.metrics.numRows = 0;
    var topRowSpace = 0;
    var bottomRowSpace = this.metrics.bottomRowSpace;
    for (var i = 0; i < this.fields.length; i++) {
      if (typeof(this.fields[i]) == 'string') { //field control commands
        switch (this.fields[i]) {
            case 'newRow':
              metrics.y += maxHeight + bottomRowSpace + topRowSpace;
              metrics.width  += this.metrics.finalSpace.x - this.metrics.fieldSpace;
              this.metrics.rows[this.metrics.numRows] = {
                width: metrics.width,
                y: metrics.y,
                height: maxHeight + bottomRowSpace + topRowSpace,
                widthType: this.metrics.widthType
              };
              this.metrics.numRows++;
              topRowSpace = this.metrics.topRowSpace;
              globalWidth = Math.max(metrics.width, globalWidth);
              metrics.width = this.metrics.initialSpace.x;
              maxHeight = 0;
              break;
            case 'globalWidth':
              this.metrics.widthType = 'globalWidth';
              break;
            case 'groupedWidth':
              this.metrics.widthType = 'groupedWidth';
              break;
            case 'singleWidth':
              this.metrics.widthType = 'singleWidth';
              break;
            default:
            throw 'Unknown width type';
        }
      } else {
        // updates incrementally metrics for dinamic fields rendering
        this.metrics.rows[this.metrics.numRows] = {
          width: metrics.width,
          y: metrics.y,
          height: maxHeight + bottomRowSpace + topRowSpace,
          widthType: this.metrics.widthType
        };
        this.fields[i].row = this.metrics.numRows;
        if (this.fields[i].rendered_) {
          this.container.add(this.fields[i].container);
        } else {
          this.fields[i].render();
        }
        box = this.fields[i].bbox();
        this.fields[i].container.move(metrics.width, metrics.y + bottomRowSpace + topRowSpace); // positions the field
        metrics.width += box.width + this.metrics.fieldSpace;
        maxHeight = Math.max(maxHeight, box.height + bottomRowSpace + topRowSpace);
      }
    }
    metrics.width += this.metrics.finalSpace.x - this.metrics.fieldSpace;
    globalWidth = Math.max(metrics.width, globalWidth);
    metrics.y += box.height + bottomRowSpace + topRowSpace + this.metrics.finalSpace.y;
    this.metrics.rows.push({
      width: metrics.width,
      y: metrics.y,
      height: maxHeight,
      widthType: this.metrics.widthType
    });
    this.width = globalWidth;
    this.height = metrics.y;
    // compute widths
    var i, j, groupFound = false, maxWidth;
    for (i = 0; i < this.metrics.rows.length; i++) {
      if (this.metrics.rows[i].widthType == 'groupedWidth') {
        if (groupFound == false) {
          maxWidth = this.metrics.rows[i].width;
          for (j=i+1;j<this.metrics.rows.length && this.metrics.rows[j].widthType == 'groupedWidth'; j++) {
            maxWidth = Math.max(maxWidth, this.metrics.rows[j].width);
          }
          groupFound = true;
        }
        this.metrics.rows[i].width = maxWidth; // uniforms width
      } else {
         if (this.metrics.rows[i].widthType == 'globalWidth') {
           this.metrics.rows[i].width = this.width; // global width
         }
         groupFound = false;
      }
    }
    // compute heights
    var height, lastWidth = this.metrics.rows[0].width;
    this.metrics.rows[0].lastRadius = 'plane';
    for (i = 1; i < this.metrics.rows.length; i++) {
      height = this.metrics.rows[i].height;
      if (lastWidth < this.metrics.rows[i].width) {
        this.metrics.rows[i].lastRadius = 'convex';
        this.metrics.rows[i-1].nextRadius = 'concave';
      }else if (lastWidth > this.metrics.rows[i].width) {
        this.metrics.rows[i].lastRadius = 'concave';
        this.metrics.rows[i-1].nextRadius = 'convex';
      } else {
        this.metrics.rows[i].lastRadius = 'plane';
        this.metrics.rows[i-1].nextRadius = 'plane';
      }
      lastWidth = this.metrics.rows[i].width;
    }
    this.metrics.rows[this.metrics.rows.length-1].nextRadius = 'convex';
  },

  //renders the block root
  renderBlock_: function() {
    //TODO: block root must be a group
    var radius = this.metrics.borderRadius; // for typing
    var rowSpace = this.metrics.rowSpace; // for typing
    if (!this.root) {
      this.root = this.container.path();
    } else {
      this.root.clear(); // if rendered clear and update segments
    }
    this.root.M({x: radius, y: this.height})
                .q({x: -radius, y: 0}, {x: -radius, y: -radius})
                .v(-this.height + 2*radius) // left side
                .q({x: 0, y: -radius}, {x: radius, y: -radius});
    // top
    var row = this.metrics.rows[0]; //for typing
    var height = row.y;
    height -= radius;
    //first row
    this.root.H(row.width - radius)
              .q({x: radius, y: 0}, {x: radius, y: radius});
    //render by row rigth line - middle
    for (var i = 1; i < this.metrics.rows.length ; i++) {
      row = this.metrics.rows[i];
      height = row.y;
      height -= radius;
      if (row.lastRadius == 'convex') {
        this.root.q({x: 0, y: radius}, {x: radius, y: radius})
                 .H(row.width - radius)
                 .q({x: radius, y: 0}, {x: radius, y: radius})
                 .V(height);
      } else if (row.lastRadius == 'concave') {
        this.root.q({x: 0, y: radius}, {x: -radius, y: radius})
                 .H(row.width + radius)
                 .q({x: -radius, y: 0}, {x: -radius, y: radius})
                 .V(height);
      } else if (row.lastRadius == 'plane') {
        this.root.V(height);
      } else {
        throw 'Unsopported type of radius: ' + row.lastRadius;
      }
    }
    var finalRow = this.metrics.rows[this.metrics.rows.length - 1]; //for typing
    var finalWidth = finalRow.width;
    // bottom line
    this.root.q({x: 0, y: radius}, {x: -radius, y: radius})
             .h(-finalWidth + 2*radius).Z();
             // TODO: report border bug in svg, when drag a block with black border, this project dont use that, use svg rect
    // this bug is resolved in chromium 44, TODO: test in chrome 42
             /*.stroke({ color: this.borderColor,
                       opacity: 1,
                       width: this.metrics.borderWidth
              }).fill(this.bgColor);*/
    this.root.fill(this.bgColor);
    this.root.addClass(this.style.className);
  },

  setColor: function(color) {
    this.bgColor = color;
    if (this.rendered_) {
      this.root.fill(color);
    }
  },

  fieldChanged: function(index) {
    // Redraw path
    this.initSvg();
    this.renderBlock_();
    /*if (this.fields[index].setSelected) {
      var tempSelected = this.fields[index].selected_;
      this.fields[index].setSelected(!tempSelected);
      this.fields[index].setSelected(tempSelected);
    }*/
  },

  onBlur: function() { // Unselect all fields
    var i, len = this.fields.length;
    for (i = 0; i < len; i++) {
      if (typeof(this.fields[i]) == 'object' && this.fields[i].setSelected) {
        this.fields[i].setSelected(false);
      }
    }
  },
  onUnRender: function() {
    var i, len = this.fields.length;
    for (i = 0; i < len; i++) {
      if (this.fields[i].unRender) {
        this.fields[i].unRender();
      }
    }
  }
});
<|endoftext|>


<commit_before>var OAuth2Strategy = require('../lib/strategy')
  , AuthorizationError = require('../lib/errors/authorizationerror')
  , TokenError = require('../lib/errors/tokenerror')
  , InternalOAuthError = require('../lib/errors/internaloautherror')
  , chai = require('chai')
  , uri = require('url');

describe('OAuth2Strategy', function() {

  describe('using default session state store', function() {

    describe('issuing authorization request', function() {
      var strategy = new OAuth2Strategy({
        authorizationURL: 'https://www.example.com/oauth2/authorize',
        tokenURL: 'https://www.example.com/oauth2/token',
        clientID: 'ABC123',
        clientSecret: 'secret',
        callbackURL: 'https://www.example.net/auth/example/callback',
        state: true
      },
      function(accessToken, refreshToken, profile, done) {});

      describe('that redirects to service provider', function() {
        var request, url;

        before(function(done) {
          chai.passport.use(strategy)
            .redirect(function(u) {
              url = u;
              done();
            })
            .req(function(req) {
              request = req;
              req.session = {};
            })
            .authenticate();
        });

        it('should be redirected', function() {
          var u = uri.parse(url, true);
          expect(u.query.state).to.have.length(24);
        });

        it('should save state in session', function() {
          var u = uri.parse(url, true);

          expect(request.session['oauth2:www.example.com'].state).to.have.length(24);
          expect(request.session['oauth2:www.example.com'].state).to.equal(u.query.state);
        });
      }); // that redirects to service provider

      describe('that redirects to service provider with other data in session', function() {
        var request, url;

        before(function(done) {
          chai.passport.use(strategy)
            .redirect(function(u) {
              url = u;
              done();
            })
            .req(function(req) {
              request = req;
              req.session = {};
              req.session['oauth2:www.example.com'] = {};
              req.session['oauth2:www.example.com'].foo = 'bar';
            })
            .authenticate();
        });

        it('should be redirected', function() {
          var u = uri.parse(url, true);
          expect(u.query.state).to.have.length(24);
        });

        it('should save state in session', function() {
          var u = uri.parse(url, true);

          expect(request.session['oauth2:www.example.com'].state).to.have.length(24);
          expect(request.session['oauth2:www.example.com'].state).to.equal(u.query.state);
        });

        it('should preserve other data in session', function() {
          expect(request.session['oauth2:www.example.com'].foo).to.equal('bar');
        });
      }); // that redirects to service provider with other data in session

      describe('that errors due to lack of session support in app', function() {
        var request, err;

        before(function(done) {
          chai.passport.use(strategy)
            .error(function(e) {
              err = e;
              done();
            })
            .req(function(req) {
              request = req;
            })
            .authenticate();
        });

        it('should error', function() {
          expect(err).to.be.an.instanceof(Error)
          expect(err.message).to.equal('OAuth2Strategy requires session support when using state. Did you forget app.use(express.session(...))?');
        });
      }); // that errors due to lack of session support in app

    }); // issuing authorization request

    describe('processing response to authorization request', function() {
      var strategy = new OAuth2Strategy({
        authorizationURL: 'https://www.example.com/oauth2/authorize',
        tokenURL: 'https://www.example.com/oauth2/token',
        clientID: 'ABC123',
        clientSecret: 'secret',
        callbackURL: 'https://www.example.net/auth/example/callback',
        state: true
      },
      function(accessToken, refreshToken, profile, done) {
        if (accessToken !== '2YotnFZFEjr1zCsicMWpAA') { return done(new Error('incorrect accessToken argument')); }
        if (refreshToken !== 'tGzv3JOkF0XG5Qx2TlKWIA') { return done(new Error('incorrect refreshToken argument')); }
        if (typeof profile !== 'object') { return done(new Error('incorrect profile argument')); }
        if (Object.keys(profile).length !== 0) { return done(new Error('incorrect profile argument')); }

        return done(null, { id: '1234' }, { message: 'Hello' });
      });

      strategy._oauth2.getOAuthAccessToken = function(code, options, callback) {
        if (code !== 'SplxlOBeZQQYbYS6WxSbIA') { return callback(new Error('incorrect code argument')); }
        if (options.grant_type !== 'authorization_code') { return callback(new Error('incorrect options.grant_type argument')); }
        if (options.redirect_uri !== 'https://www.example.net/auth/example/callback') { return callback(new Error('incorrect options.redirect_uri argument')); }

        return callback(null, '2YotnFZFEjr1zCsicMWpAA', 'tGzv3JOkF0XG5Qx2TlKWIA', { token_type: 'example' });
      }

      describe('that was approved', function() {
        var request
          , user
          , info;

        before(function(done) {
          chai.passport.use(strategy)
            .success(function(u, i) {
              user = u;
              info = i;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
              req.session = {};
              req.session['oauth2:www.example.com'] = {};
              req.session['oauth2:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK';
            })
            .authenticate();
        });

        it('should supply user', function() {
          expect(user).to.be.an.object;
          expect(user.id).to.equal('1234');
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Hello');
        });

        it('should remove state from session', function() {
          expect(request.session['oauth2:www.example.com']).to.be.undefined;
        });
      }); // that was approved

      describe('that fails due to state being invalid', function() {
        var request
          , info, status;

        before(function(done) {
          chai.passport.use(strategy)
            .fail(function(i, s) {
              info = i;
              status = s;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG';
              req.session = {};
              req.session['oauth2:www.example.com'] = {};
              req.session['oauth2:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK';
            })
            .authenticate();
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Invalid authorization request state.');
        });

        it('should supply status', function() {
          expect(status).to.equal(403);
        });

        it('should remove state from session', function() {
          expect(request.session['oauth2:www.example.com']).to.be.undefined;
        });
      }); // that fails due to state being invalid

      describe('that fails due to provider-specific state not found in session', function() {
        var request
          , info, status;

        before(function(done) {
          chai.passport.use(strategy)
            .fail(function(i, s) {
              info = i;
              status = s;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
              req.session = {};
            })
            .authenticate();
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Unable to verify authorization request state.');
        });

        it('should supply status', function() {
          expect(status).to.equal(403);
        });
      }); // that fails due to state not found in session

      describe('that fails due to provider-specific state lacking state value', function() {
        var request
          , info, status;

        before(function(done) {
          chai.passport.use(strategy)
            .fail(function(i, s) {
              info = i;
              status = s;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
              req.session = {};
              req.session['oauth2:www.example.com'] = {};
            })
            .authenticate();
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Unable to verify authorization request state.');
        });

        it('should supply status', function() {
          expect(status).to.equal(403);
        });
      }); // that fails due to provider-specific state lacking state value

      describe('that errors due to lack of session support in app', function() {
        var request
          , err;

        before(function(done) {
          chai.passport.use(strategy)
            .error(function(e) {
              err = e;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
            })
            .authenticate();
        });

        it('should error', function() {
          expect(err).to.be.an.instanceof(Error)
          expect(err.message).to.equal('OAuth2Strategy requires session support when using state. Did you forget app.use(express.session(...))?');
        });
      }); // that errors due to lack of session support in app

    }); // processing response to authorization request

  }); // using default session state store

  describe('using default session state store with session key option', function() {
    var strategy = new OAuth2Strategy({
      authorizationURL: 'https://www.example.com/oauth2/authorize',
      tokenURL: 'https://www.example.com/oauth2/token',
      clientID: 'ABC123',
      clientSecret: 'secret',
      callbackURL: 'https://www.example.net/auth/example/callback',
      state: true,
      sessionKey: 'oauth2:example'
    },
    function(accessToken, refreshToken, profile, done) {
      if (accessToken !== '2YotnFZFEjr1zCsicMWpAA') { return done(new Error('incorrect accessToken argument')); }
      if (refreshToken !== 'tGzv3JOkF0XG5Qx2TlKWIA') { return done(new Error('incorrect refreshToken argument')); }
      if (typeof profile !== 'object') { return done(new Error('incorrect profile argument')); }
      if (Object.keys(profile).length !== 0) { return done(new Error('incorrect profile argument')); }

      return done(null, { id: '1234' }, { message: 'Hello' });
    });

    strategy._oauth2.getOAuthAccessToken = function(code, options, callback) {
      if (code !== 'SplxlOBeZQQYbYS6WxSbIA') { return callback(new Error('incorrect code argument')); }
      if (options.grant_type !== 'authorization_code') { return callback(new Error('incorrect options.grant_type argument')); }
      if (options.redirect_uri !== 'https://www.example.net/auth/example/callback') { return callback(new Error('incorrect options.redirect_uri argument')); }

      return callback(null, '2YotnFZFEjr1zCsicMWpAA', 'tGzv3JOkF0XG5Qx2TlKWIA', { token_type: 'example' });
    }

    describe('issuing authorization request', function() {

      describe('that redirects to service provider', function() {
        var request, url;

        before(function(done) {
          chai.passport.use(strategy)
            .redirect(function(u) {
              url = u;
              done();
            })
            .req(function(req) {
              request = req;
              req.session = {};
            })
            .authenticate();
        });

        it('should be redirected', function() {
          var u = uri.parse(url, true);
          expect(u.query.state).to.have.length(24);
        });

        it('should save state in session', function() {
          var u = uri.parse(url, true);

          expect(request.session['oauth2:example'].state).to.have.length(24);
          expect(request.session['oauth2:example'].state).to.equal(u.query.state);
        });
      }); // that redirects to service provider

    }); // issuing authorization request

    describe('processing response to authorization request', function() {

      describe('that was approved', function() {
        var request
          , user
          , info;

        before(function(done) {
          chai.passport.use(strategy)
            .success(function(u, i) {
              user = u;
              info = i;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
              req.session = {};
              req.session['oauth2:example'] = {};
              req.session['oauth2:example']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK';
            })
            .authenticate();
        });

        it('should supply user', function() {
          expect(user).to.be.an.object;
          expect(user.id).to.equal('1234');
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Hello');
        });

        it('should remove state from session', function() {
          expect(request.session['oauth2:example']).to.be.undefined;
        });
      }); // that was approved

    }); // processing response to authorization request

  }); // using default session state store with session key option

});
<commit_msg>Expand test coverage.<commit_after>var OAuth2Strategy = require('../lib/strategy')
  , AuthorizationError = require('../lib/errors/authorizationerror')
  , TokenError = require('../lib/errors/tokenerror')
  , InternalOAuthError = require('../lib/errors/internaloautherror')
  , chai = require('chai')
  , uri = require('url');

describe('OAuth2Strategy', function() {

  describe('using default session state store', function() {

    describe('issuing authorization request', function() {
      var strategy = new OAuth2Strategy({
        authorizationURL: 'https://www.example.com/oauth2/authorize',
        tokenURL: 'https://www.example.com/oauth2/token',
        clientID: 'ABC123',
        clientSecret: 'secret',
        callbackURL: 'https://www.example.net/auth/example/callback',
        state: true
      },
      function(accessToken, refreshToken, profile, done) {});

      describe('that redirects to service provider', function() {
        var request, url;

        before(function(done) {
          chai.passport.use(strategy)
            .redirect(function(u) {
              url = u;
              done();
            })
            .req(function(req) {
              request = req;
              req.session = {};
            })
            .authenticate();
        });

        it('should be redirected', function() {
          var u = uri.parse(url, true);
          expect(u.query.state).to.have.length(24);
        });

        it('should save state in session', function() {
          var u = uri.parse(url, true);

          expect(request.session['oauth2:www.example.com'].state).to.have.length(24);
          expect(request.session['oauth2:www.example.com'].state).to.equal(u.query.state);
        });
      }); // that redirects to service provider

      describe('that redirects to service provider with other data in session', function() {
        var request, url;

        before(function(done) {
          chai.passport.use(strategy)
            .redirect(function(u) {
              url = u;
              done();
            })
            .req(function(req) {
              request = req;
              req.session = {};
              req.session['oauth2:www.example.com'] = {};
              req.session['oauth2:www.example.com'].foo = 'bar';
            })
            .authenticate();
        });

        it('should be redirected', function() {
          var u = uri.parse(url, true);
          expect(u.query.state).to.have.length(24);
        });

        it('should save state in session', function() {
          var u = uri.parse(url, true);

          expect(request.session['oauth2:www.example.com'].state).to.have.length(24);
          expect(request.session['oauth2:www.example.com'].state).to.equal(u.query.state);
        });

        it('should preserve other data in session', function() {
          expect(request.session['oauth2:www.example.com'].foo).to.equal('bar');
        });
      }); // that redirects to service provider with other data in session

      describe('that errors due to lack of session support in app', function() {
        var request, err;

        before(function(done) {
          chai.passport.use(strategy)
            .error(function(e) {
              err = e;
              done();
            })
            .req(function(req) {
              request = req;
            })
            .authenticate();
        });

        it('should error', function() {
          expect(err).to.be.an.instanceof(Error)
          expect(err.message).to.equal('OAuth2Strategy requires session support when using state. Did you forget app.use(express.session(...))?');
        });
      }); // that errors due to lack of session support in app

    }); // issuing authorization request

    describe('processing response to authorization request', function() {
      var strategy = new OAuth2Strategy({
        authorizationURL: 'https://www.example.com/oauth2/authorize',
        tokenURL: 'https://www.example.com/oauth2/token',
        clientID: 'ABC123',
        clientSecret: 'secret',
        callbackURL: 'https://www.example.net/auth/example/callback',
        state: true
      },
      function(accessToken, refreshToken, profile, done) {
        if (accessToken !== '2YotnFZFEjr1zCsicMWpAA') { return done(new Error('incorrect accessToken argument')); }
        if (refreshToken !== 'tGzv3JOkF0XG5Qx2TlKWIA') { return done(new Error('incorrect refreshToken argument')); }
        if (typeof profile !== 'object') { return done(new Error('incorrect profile argument')); }
        if (Object.keys(profile).length !== 0) { return done(new Error('incorrect profile argument')); }

        return done(null, { id: '1234' }, { message: 'Hello' });
      });

      strategy._oauth2.getOAuthAccessToken = function(code, options, callback) {
        if (code !== 'SplxlOBeZQQYbYS6WxSbIA') { return callback(new Error('incorrect code argument')); }
        if (options.grant_type !== 'authorization_code') { return callback(new Error('incorrect options.grant_type argument')); }
        if (options.redirect_uri !== 'https://www.example.net/auth/example/callback') { return callback(new Error('incorrect options.redirect_uri argument')); }

        return callback(null, '2YotnFZFEjr1zCsicMWpAA', 'tGzv3JOkF0XG5Qx2TlKWIA', { token_type: 'example' });
      }

      describe('that was approved', function() {
        var request
          , user
          , info;

        before(function(done) {
          chai.passport.use(strategy)
            .success(function(u, i) {
              user = u;
              info = i;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
              req.session = {};
              req.session['oauth2:www.example.com'] = {};
              req.session['oauth2:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK';
            })
            .authenticate();
        });

        it('should supply user', function() {
          expect(user).to.be.an.object;
          expect(user.id).to.equal('1234');
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Hello');
        });

        it('should remove state from session', function() {
          expect(request.session['oauth2:www.example.com']).to.be.undefined;
        });
      }); // that was approved

      describe('that was approved with other data in the session', function() {
        var request
          , user
          , info;

        before(function(done) {
          chai.passport.use(strategy)
            .success(function(u, i) {
              user = u;
              info = i;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
              req.session = {};
              req.session['oauth2:www.example.com'] = {};
              req.session['oauth2:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK';
              req.session['oauth2:www.example.com'].foo = 'bar';
            })
            .authenticate();
        });

        it('should supply user', function() {
          expect(user).to.be.an.object;
          expect(user.id).to.equal('1234');
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Hello');
        });

        it('should preserve other data from session', function() {
          expect(request.session['oauth2:www.example.com'].state).to.be.undefined;
          expect(request.session['oauth2:www.example.com'].foo).to.equal('bar');
        });
      }); // that was approved with other data in the session

      describe('that fails due to state being invalid', function() {
        var request
          , info, status;

        before(function(done) {
          chai.passport.use(strategy)
            .fail(function(i, s) {
              info = i;
              status = s;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK-WRONG';
              req.session = {};
              req.session['oauth2:www.example.com'] = {};
              req.session['oauth2:www.example.com']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK';
            })
            .authenticate();
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Invalid authorization request state.');
        });

        it('should supply status', function() {
          expect(status).to.equal(403);
        });

        it('should remove state from session', function() {
          expect(request.session['oauth2:www.example.com']).to.be.undefined;
        });
      }); // that fails due to state being invalid

      describe('that fails due to provider-specific state not found in session', function() {
        var request
          , info, status;

        before(function(done) {
          chai.passport.use(strategy)
            .fail(function(i, s) {
              info = i;
              status = s;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
              req.session = {};
            })
            .authenticate();
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Unable to verify authorization request state.');
        });

        it('should supply status', function() {
          expect(status).to.equal(403);
        });
      }); // that fails due to state not found in session

      describe('that fails due to provider-specific state lacking state value', function() {
        var request
          , info, status;

        before(function(done) {
          chai.passport.use(strategy)
            .fail(function(i, s) {
              info = i;
              status = s;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
              req.session = {};
              req.session['oauth2:www.example.com'] = {};
            })
            .authenticate();
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Unable to verify authorization request state.');
        });

        it('should supply status', function() {
          expect(status).to.equal(403);
        });
      }); // that fails due to provider-specific state lacking state value

      describe('that errors due to lack of session support in app', function() {
        var request
          , err;

        before(function(done) {
          chai.passport.use(strategy)
            .error(function(e) {
              err = e;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
            })
            .authenticate();
        });

        it('should error', function() {
          expect(err).to.be.an.instanceof(Error)
          expect(err.message).to.equal('OAuth2Strategy requires session support when using state. Did you forget app.use(express.session(...))?');
        });
      }); // that errors due to lack of session support in app

    }); // processing response to authorization request

  }); // using default session state store

  describe('using default session state store with session key option', function() {
    var strategy = new OAuth2Strategy({
      authorizationURL: 'https://www.example.com/oauth2/authorize',
      tokenURL: 'https://www.example.com/oauth2/token',
      clientID: 'ABC123',
      clientSecret: 'secret',
      callbackURL: 'https://www.example.net/auth/example/callback',
      state: true,
      sessionKey: 'oauth2:example'
    },
    function(accessToken, refreshToken, profile, done) {
      if (accessToken !== '2YotnFZFEjr1zCsicMWpAA') { return done(new Error('incorrect accessToken argument')); }
      if (refreshToken !== 'tGzv3JOkF0XG5Qx2TlKWIA') { return done(new Error('incorrect refreshToken argument')); }
      if (typeof profile !== 'object') { return done(new Error('incorrect profile argument')); }
      if (Object.keys(profile).length !== 0) { return done(new Error('incorrect profile argument')); }

      return done(null, { id: '1234' }, { message: 'Hello' });
    });

    strategy._oauth2.getOAuthAccessToken = function(code, options, callback) {
      if (code !== 'SplxlOBeZQQYbYS6WxSbIA') { return callback(new Error('incorrect code argument')); }
      if (options.grant_type !== 'authorization_code') { return callback(new Error('incorrect options.grant_type argument')); }
      if (options.redirect_uri !== 'https://www.example.net/auth/example/callback') { return callback(new Error('incorrect options.redirect_uri argument')); }

      return callback(null, '2YotnFZFEjr1zCsicMWpAA', 'tGzv3JOkF0XG5Qx2TlKWIA', { token_type: 'example' });
    }

    describe('issuing authorization request', function() {

      describe('that redirects to service provider', function() {
        var request, url;

        before(function(done) {
          chai.passport.use(strategy)
            .redirect(function(u) {
              url = u;
              done();
            })
            .req(function(req) {
              request = req;
              req.session = {};
            })
            .authenticate();
        });

        it('should be redirected', function() {
          var u = uri.parse(url, true);
          expect(u.query.state).to.have.length(24);
        });

        it('should save state in session', function() {
          var u = uri.parse(url, true);

          expect(request.session['oauth2:example'].state).to.have.length(24);
          expect(request.session['oauth2:example'].state).to.equal(u.query.state);
        });
      }); // that redirects to service provider

    }); // issuing authorization request

    describe('processing response to authorization request', function() {

      describe('that was approved', function() {
        var request
          , user
          , info;

        before(function(done) {
          chai.passport.use(strategy)
            .success(function(u, i) {
              user = u;
              info = i;
              done();
            })
            .req(function(req) {
              request = req;

              req.query = {};
              req.query.code = 'SplxlOBeZQQYbYS6WxSbIA';
              req.query.state = 'DkbychwKu8kBaJoLE5yeR5NK';
              req.session = {};
              req.session['oauth2:example'] = {};
              req.session['oauth2:example']['state'] = 'DkbychwKu8kBaJoLE5yeR5NK';
            })
            .authenticate();
        });

        it('should supply user', function() {
          expect(user).to.be.an.object;
          expect(user.id).to.equal('1234');
        });

        it('should supply info', function() {
          expect(info).to.be.an.object;
          expect(info.message).to.equal('Hello');
        });

        it('should remove state from session', function() {
          expect(request.session['oauth2:example']).to.be.undefined;
        });
      }); // that was approved

    }); // processing response to authorization request

  }); // using default session state store with session key option

});
<|endoftext|>


<commit_before>// Declare internals

var internals = {};

// Plugin registration

exports.register = function (plugin, options, next) {

    plugin.select('test').route({ path: '/test1', method: 'GET', handler: function (request, reply) { reply('testing123' + ((plugin.settings.app && plugin.settings.app.my) || '')); } });
    plugin.expose(internals.math);
    plugin.expose('glue', internals.text.glue);
    plugin.expose('prefix', plugin.config.routes.prefix);

    return next();
};

exports.register.attributes = {
    name: '--test1',
    version: '1.0.0'
};

internals.math = {
    add: function (a, b) {

        return a + b;
    }
};

internals.text = {
    glue: function (a, b) {

        return a + b;
    }
};
<commit_msg>Update tests to use realm.modifiers of hapijs/hapi#2230<commit_after>// Declare internals

var internals = {};

// Plugin registration

exports.register = function (plugin, options, next) {

    plugin.select('test').route({ path: '/test1', method: 'GET', handler: function (request, reply) { reply('testing123' + ((plugin.settings.app && plugin.settings.app.my) || '')); } });
    plugin.expose(internals.math);
    plugin.expose('glue', internals.text.glue);
    plugin.expose('prefix', plugin.realm.modifiers.route.prefix);

    return next();
};

exports.register.attributes = {
    name: '--test1',
    version: '1.0.0'
};

internals.math = {
    add: function (a, b) {

        return a + b;
    }
};

internals.text = {
    glue: function (a, b) {

        return a + b;
    }
};
<|endoftext|>


<commit_before>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
'use strict';

const path = require('path');

let resolveCache = new Map();
function useForks(forks) {
  let resolvedForks = {};
  Object.keys(forks).forEach(srcModule => {
    const targetModule = forks[srcModule];
    resolvedForks[require.resolve(srcModule)] = require.resolve(targetModule);
  });
  return {
    resolveId(importee, importer) {
      if (!importer || !importee) {
        return null;
      }
      let resolvedImportee = null;
      let cacheKey = `${importer}:::${importee}`;
      if (resolveCache.has(cacheKey)) {
        // Avoid hitting file system if possible.
        resolvedImportee = resolveCache.get(cacheKey);
      } else {
        try {
          resolvedImportee = require.resolve(importee, {
            paths: [path.dirname(importer)],
          });
        } catch (err) {
          // Not our fault, let Rollup fail later.
        }
        if (resolvedImportee) {
          resolveCache.set(cacheKey, resolvedImportee);
        }
      }
      if (resolvedImportee && resolvedForks.hasOwnProperty(resolvedImportee)) {
        // We found a fork!
        return resolvedForks[resolvedImportee];
      }
      return null;
    },
  };
}

module.exports = useForks;
<commit_msg>Fix issues with the new fork plugin (#11723)<commit_after>/**
 * Copyright (c) 2013-present, Facebook, Inc.
 *
 * This source code is licensed under the MIT license found in the
 * LICENSE file in the root directory of this source tree.
 */
'use strict';

const path = require('path');
const semver = require('semver');

function resolveRelatively(importee, importer) {
  if (semver.gte(process.version, '8.9.0')) {
    return require.resolve(importee, {
      paths: [path.dirname(importer)],
    });
  } else {
    // `paths` argument is not available in older Node.
    // This works though.
    // https://github.com/nodejs/node/issues/5963
    const Module = require('module');
    return Module._findPath(importee, [
      path.dirname(importer),
      ...module.paths,
    ]);
  }
}

let resolveCache = new Map();
function useForks(forks) {
  let resolvedForks = new Map();
  Object.keys(forks).forEach(srcModule => {
    const targetModule = forks[srcModule];
    resolvedForks.set(
      require.resolve(srcModule),
      require.resolve(targetModule)
    );
  });
  return {
    resolveId(importee, importer) {
      if (!importer || !importee) {
        return null;
      }
      if (importee.startsWith('\u0000')) {
        // Internal Rollup reference, ignore.
        // Passing that to Node file functions can fatal.
        return null;
      }
      let resolvedImportee = null;
      let cacheKey = `${importer}:::${importee}`;
      if (resolveCache.has(cacheKey)) {
        // Avoid hitting file system if possible.
        resolvedImportee = resolveCache.get(cacheKey);
      } else {
        try {
          resolvedImportee = resolveRelatively(importee, importer);
        } catch (err) {
          // Not our fault, let Rollup fail later.
        }
        if (resolvedImportee) {
          resolveCache.set(cacheKey, resolvedImportee);
        }
      }
      if (resolvedImportee && resolvedForks.has(resolvedImportee)) {
        // We found a fork!
        return resolvedForks.get(resolvedImportee);
      }
      return null;
    },
  };
}

module.exports = useForks;
<|endoftext|>


<commit_before>import {assert} from 'chai';
import {getNthOfTypeSelector} from '../src/selector-nth-of-type';
import {getCssSelector} from '../src';

describe('selector - nth-of-type', function () {

  let root;

  beforeEach(function () {
    root = document.body.appendChild(document.createElement('div'));
  });

  afterEach(function () {
    root.parentNode.removeChild(root);
  });

  it('should generate nth-child selector', function () {
    root.innerHTML = '

';
    const result = getNthOfTypeSelector(root.lastChild);
    assert.equal(result.length, 1);
    assert.equal(result[0], 'p:nth-of-type(2)');
  });

  it('should not include tag if nthoftype is used', function () {
    root.innerHTML = `

    `;
    const result = getCssSelector(root.querySelector('a'), {
      selectors: ['tag', 'nthoftype'],
      root,
    });
    assert.equal(result, ':nth-child(1) > :nth-child(1) > :nth-child(1)');
  });

  it('should not collide with tag selector', function () {
    root.innerHTML = '

';
    const result = getCssSelector(root.lastChild, {
      selectors: ['tag', 'nthoftype'],
      root,
    });
    assert.equal(result, 'p:nth-of-type(2)');
  });

  it('should not collide with `includeTag` option', function () {
    root.innerHTML = '

';
    const result = getCssSelector(root.lastChild, {
      selectors: ['nthoftype'],
      includeTag: true,
      root,
    });
    assert.equal(result, 'p:nth-of-type(2)');
  });

  it('should not throw for element without parent', function () {
    const element = document.createElement('div');
    const fn = function () {
      getCssSelector(element, {
        selectors: ['nthoftype'],
        root,
      });
    };
    assert.doesNotThrow(fn);
  });

  it('should not throw for element directly in document', function () {
    const element = document.querySelector('html');
    const fn = function () {
      getCssSelector(element, {
        selectors: ['nthoftype'],
        root,
      });
    };
    assert.doesNotThrow(fn);
  });

});
<commit_msg>test: adjust test<commit_after>import {assert} from 'chai';
import {getNthOfTypeSelector} from '../src/selector-nth-of-type';
import {getCssSelector} from '../src';
import {constructSelector} from '../src/utilities-selectors.js';

describe('selector - nth-of-type', function () {

  let root;

  beforeEach(function () {
    root = document.body.appendChild(document.createElement('div'));
  });

  afterEach(function () {
    root.parentNode.removeChild(root);
  });

  it('should generate nth-child selector', function () {
    root.innerHTML = '

';
    const result = getNthOfTypeSelector(root.lastChild);
    assert.equal(result.length, 1);
    assert.equal(result[0], 'p:nth-of-type(2)');
  });

  it('should not include tag if nthoftype is used', function () {
    const result = constructSelector({
      tag: ['div'],
      nthoftype: ['div:nth-of-type(1)']
    });
    assert.equal(result, 'div:nth-of-type(1)');
  });

  it('should not collide with tag selector', function () {
    root.innerHTML = '

';
    const result = getCssSelector(root.lastChild, {
      selectors: ['tag', 'nthoftype'],
      root,
    });
    assert.equal(result, 'p:nth-of-type(2)');
  });

  it('should not collide with `includeTag` option', function () {
    root.innerHTML = '

';
    const result = getCssSelector(root.lastChild, {
      selectors: ['nthoftype'],
      includeTag: true,
      root,
    });
    assert.equal(result, 'p:nth-of-type(2)');
  });

  it('should not throw for element without parent', function () {
    const element = document.createElement('div');
    const fn = function () {
      getCssSelector(element, {
        selectors: ['nthoftype'],
        root,
      });
    };
    assert.doesNotThrow(fn);
  });

  it('should not throw for element directly in document', function () {
    const element = document.querySelector('html');
    const fn = function () {
      getCssSelector(element, {
        selectors: ['nthoftype'],
        root,
      });
    };
    assert.doesNotThrow(fn);
  });

});
<|endoftext|>


<commit_before>'use strict';

describe('Controller: DocumentCtrl', function () {

  // load the controller's module
  beforeEach(module('lorryApp'));

  var DocumentCtrl,
    scope,
    yamlValidator;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope, _yamlValidator_) {
    scope = $rootScope.$new();
    DocumentCtrl = $controller('DocumentCtrl', {
      $scope: scope
    });
    yamlValidator = _yamlValidator_;
  }));

  describe('$scope.validateYaml', function() {

    describe('when validation succeeds', function() {
      var deferredSuccess;

      beforeEach(inject(function($q) {
        deferredSuccess = $q.defer();
        spyOn(yamlValidator, 'validate').and.returnValue(deferredSuccess.promise);
        scope.validateYaml();
        deferredSuccess.resolve({data: {lines: ['line'], errors: ['error']}});
        scope.$digest();
      }));

      it ('adds validation data to the scope', function() {
        expect(scope.yamlDocument.lines).toEqual(['line']);
        expect(scope.yamlDocument.errors).toEqual(['error']);
      });

      it ('builds the service definitions', function() {
        expect(scope.serviceDefinitions).toBeDefined();
        expect(scope.serviceDefinitions).toContain([{ text: 'line', lineNumber: 1, errors: [  ] }]);
      });

      it ('sets the parseErrors flag for message display true', function () {
        expect(scope.yamlDocument.parseErrors).toBe(true);
      });
    });

    describe('when validation fails', function() {
      var deferredError;

      beforeEach(inject(function($q) {
        deferredError = $q.defer();
        spyOn(yamlValidator, 'validate').and.returnValue(deferredError.promise);
      }));

      describe('due to a status other than 500 (e.g. 422)', function () {
        beforeEach(inject(function() {
          scope.validateYaml();
          deferredError.reject({data: {error: 'something went wrong'}});
          scope.$digest();
        }));

        it ('adds the error to the scope', function() {
          expect(scope.yamlDocument.lines).toBeUndefined();
          expect(scope.yamlDocument.errors).toEqual([{error: {message: 'something went wrong'}}]);
        });

        it ('does not build the service definitions', function () {
          expect(scope.serviceDefinitions).toEqual([]);
        });

        it ('sets the parseErrors flag for message display true', function () {
          expect(scope.yamlDocument.loadFailure).toBe(true);
        });
      });

      describe('when validation fails because of a 500 error', function () {
        beforeEach(inject(function() {
          scope.validateYaml();
          deferredError.reject({status: 500, data: {error: 'something went wrong'}});
          scope.$digest();
        }));

        it ('adds an error to yamlDocument.errors indicating internal server error', function () {
          expect(scope.yamlDocument.errors).toEqual([{error: {message: 'An internal server error has occurred'}}]);
        });

        it ('sets the parseErrors flag for message display true', function () {
          expect(scope.yamlDocument.loadFailure).toBe(true);
        });

      });
    });

  });

  describe('$scope.resetWorkspace', function () {
    it('resets the $scope.yamlDocument to an empty object', function () {
      scope.yamlDocument = { foo: 'bar' };
      expect(scope.yamlDocument).toEqual({ foo: 'bar' });
      scope.resetWorkspace();
      expect(scope.yamlDocument).toEqual({});
    });
  });

  describe('yamlDocument.raw watcher', function () {
    beforeEach(function () {
      scope.resettable = false;
      scope.importable = true;
      scope.yamlDocument = {};
    });

    describe('when $scope.yamlDocument.raw is undefined', function () {
      beforeEach(function () {
        scope.yamlDocument.raw = undefined;
        scope.$digest();
      });

      it('sets $scope.resettable false', function () {
        expect(scope.resettable).toBe(false);
      });

      it('sets $scope.importable true', function () {
        expect(scope.importable).toBe(true);
      });
    });

    describe('when $scope.yamlDocument.raw is defined', function () {
      beforeEach(function () {
        scope.yamlDocument.raw = 'asdf';
        scope.$digest();
      });

      it('sets $scope.resettable true', function () {
        expect(scope.resettable).toBe(true);
      });

      it('sets $scope.importable false', function () {
        expect(scope.importable).toBe(false);
      });
    });
  });

  describe('$scope.serviceName', function () {

    it('returns the service name', function () {
      var serviceDef =
        [
          {
            text: 'db:\\n',
            lineNumber: 1,
            errors: [
              { error: { message: 'error1', line: 1, column: 2} }
            ]
          },
          {
            text: '  image: postgres:latest\\n',
            lineNumber: 2,
            errors: [
              { error: { message: 'error2', line: 2, column: 3} }
            ]
          }
        ];

      var sName = scope.serviceName(serviceDef);

      expect(sName).toEqual('db');
    });
  });

  describe('$scope.validateJson', function () {
    describe('when the yamlDocument has no/empty json', function () {
      beforeEach(function () {
        spyOn(scope, 'resetWorkspace');
        spyOn(scope, 'validateYaml');
      });

      it('resets the workspace', function () {
        scope.yamlDocument.json = {};
        scope.validateJson();
        expect(scope.resetWorkspace).toHaveBeenCalled();
      });
    });

    describe('when the yamlDocument has json', function () {
      beforeEach(function () {
        spyOn(scope, 'validateYaml');
        scope.yamlDocument.raw = null;
        scope.yamlDocument.json = {'foo': 'bar'};
        scope.validateJson();
      });

      it('stores the yamlized json into the yamlDocument.raw property', function () {
        expect(scope.yamlDocument.raw).not.toBeNull();
      });

      it('calls validatesYaml', function () {
        expect(scope.validateYaml).toHaveBeenCalled();
      });
    });
  });

  describe('deleteService event handler', function () {
    beforeEach(function () {
      spyOn(scope, 'validateJson');
    });

    describe('when the yamlDocument.json has a service matching the serviceName in the event', function () {
      beforeEach(function () {
        scope.yamlDocument.json = { someService: [] };
        scope.$emit('deleteService', 'someService');
      });

      it('should not change the yamlDocument.json', function () {
        expect(scope.yamlDocument.json).toEqual({});
      });

      it('should not call validateJson', function () {
        expect(scope.validateJson).toHaveBeenCalled();
      });
    });

    describe('when the yamlDocument.json does not have a service matching the serviceName in the event', function () {
      beforeEach(function () {
        scope.yamlDocument.json = { someOtherService: [] };
        scope.$emit('deleteService', 'someService');
      });

      it('should not change the yamlDocument.json', function () {
        expect(scope.yamlDocument.json).toEqual({ someOtherService: [] });
      });

      it('should not call validateJson', function () {
        expect(scope.validateJson).not.toHaveBeenCalled();
      });
    });
  });

});
<commit_msg>adding the ability to delete a service definition via its action menu<commit_after>'use strict';

describe('Controller: DocumentCtrl', function () {

  // load the controller's module
  beforeEach(module('lorryApp'));

  var DocumentCtrl,
    scope,
    yamlValidator;

  // Initialize the controller and a mock scope
  beforeEach(inject(function ($controller, $rootScope, _yamlValidator_) {
    scope = $rootScope.$new();
    DocumentCtrl = $controller('DocumentCtrl', {
      $scope: scope
    });
    yamlValidator = _yamlValidator_;
  }));

  describe('$scope.validateYaml', function() {

    describe('when validation succeeds', function() {
      var deferredSuccess;

      beforeEach(inject(function($q) {
        deferredSuccess = $q.defer();
        spyOn(yamlValidator, 'validate').and.returnValue(deferredSuccess.promise);
        scope.validateYaml();
        deferredSuccess.resolve({data: {lines: ['line'], errors: ['error']}});
        scope.$digest();
      }));

      it ('adds validation data to the scope', function() {
        expect(scope.yamlDocument.lines).toEqual(['line']);
        expect(scope.yamlDocument.errors).toEqual(['error']);
      });

      it ('builds the service definitions', function() {
        expect(scope.serviceDefinitions).toBeDefined();
        expect(scope.serviceDefinitions).toContain([{ text: 'line', lineNumber: 1, errors: [  ] }]);
      });

      it ('sets the parseErrors flag for message display true', function () {
        expect(scope.yamlDocument.parseErrors).toBe(true);
      });
    });

    describe('when validation fails', function() {
      var deferredError;

      beforeEach(inject(function($q) {
        deferredError = $q.defer();
        spyOn(yamlValidator, 'validate').and.returnValue(deferredError.promise);
      }));

      describe('due to a status other than 500 (e.g. 422)', function () {
        beforeEach(inject(function() {
          scope.validateYaml();
          deferredError.reject({data: {error: 'something went wrong'}});
          scope.$digest();
        }));

        it ('adds the error to the scope', function() {
          expect(scope.yamlDocument.lines).toBeUndefined();
          expect(scope.yamlDocument.errors).toEqual([{error: {message: 'something went wrong'}}]);
        });

        it ('does not build the service definitions', function () {
          expect(scope.serviceDefinitions).toEqual([]);
        });

        it ('sets the parseErrors flag for message display true', function () {
          expect(scope.yamlDocument.loadFailure).toBe(true);
        });
      });

      describe('when validation fails because of a 500 error', function () {
        beforeEach(inject(function() {
          scope.validateYaml();
          deferredError.reject({status: 500, data: {error: 'something went wrong'}});
          scope.$digest();
        }));

        it ('adds an error to yamlDocument.errors indicating internal server error', function () {
          expect(scope.yamlDocument.errors).toEqual([{error: {message: 'An internal server error has occurred'}}]);
        });

        it ('sets the parseErrors flag for message display true', function () {
          expect(scope.yamlDocument.loadFailure).toBe(true);
        });

      });
    });

  });

  describe('$scope.resetWorkspace', function () {
    it('resets the $scope.yamlDocument to an empty object', function () {
      scope.yamlDocument = { foo: 'bar' };
      expect(scope.yamlDocument).toEqual({ foo: 'bar' });
      scope.resetWorkspace();
      expect(scope.yamlDocument).toEqual({});
    });
  });

  describe('yamlDocument.raw watcher', function () {
    beforeEach(function () {
      scope.resettable = false;
      scope.importable = true;
      scope.yamlDocument = {};
    });

    describe('when $scope.yamlDocument.raw is undefined', function () {
      beforeEach(function () {
        scope.yamlDocument.raw = undefined;
        scope.$digest();
      });

      it('sets $scope.resettable false', function () {
        expect(scope.resettable).toBe(false);
      });

      it('sets $scope.importable true', function () {
        expect(scope.importable).toBe(true);
      });
    });

    describe('when $scope.yamlDocument.raw is defined', function () {
      beforeEach(function () {
        scope.yamlDocument.raw = 'asdf';
        scope.$digest();
      });

      it('sets $scope.resettable true', function () {
        expect(scope.resettable).toBe(true);
      });

      it('sets $scope.importable false', function () {
        expect(scope.importable).toBe(false);
      });
    });
  });

  describe('$scope.serviceName', function () {

    it('returns the service name', function () {
      var serviceDef =
        [
          {
            text: 'db:\\n',
            lineNumber: 1,
            errors: [
              { error: { message: 'error1', line: 1, column: 2} }
            ]
          },
          {
            text: '  image: postgres:latest\\n',
            lineNumber: 2,
            errors: [
              { error: { message: 'error2', line: 2, column: 3} }
            ]
          }
        ];

      var sName = scope.serviceName(serviceDef);

      expect(sName).toEqual('db');
    });
  });

  describe('$scope.validateJson', function () {
    describe('when the yamlDocument has no/empty json', function () {
      beforeEach(function () {
        spyOn(scope, 'resetWorkspace');
        spyOn(scope, 'validateYaml');
      });

      it('resets the workspace', function () {
        scope.yamlDocument.json = {};
        scope.validateJson();
        expect(scope.resetWorkspace).toHaveBeenCalled();
      });
    });

    describe('when the yamlDocument has json', function () {
      beforeEach(function () {
        spyOn(scope, 'validateYaml');
        scope.yamlDocument.raw = null;
        scope.yamlDocument.json = {'foo': 'bar'};
        scope.validateJson();
      });

      it('stores the yamlized json into the yamlDocument.raw property', function () {
        expect(scope.yamlDocument.raw).not.toBeNull();
      });

      it('calls validatesYaml', function () {
        expect(scope.validateYaml).toHaveBeenCalled();
      });
    });
  });

  describe('deleteService event handler', function () {
    beforeEach(function () {
      spyOn(scope, 'validateJson');
    });

    describe('when the yamlDocument.json has a service matching the serviceName in the event', function () {
      beforeEach(function () {
        scope.yamlDocument.json = { someService: [] };
        scope.$emit('deleteService', 'someService');
      });

      it('should not change the yamlDocument.json', function () {
        expect(scope.yamlDocument.json).toEqual({});
      });

      it('should not call validateJson', function () {
        expect(scope.validateJson).toHaveBeenCalled();
      });
    });

    describe('when the yamlDocument.json does not have a service matching the serviceName in the event', function () {
      beforeEach(function () {
        scope.yamlDocument.json = { someOtherService: [] };
        scope.$emit('deleteService', 'someService');
      });

      it('should not change the yamlDocument.json', function () {
        expect(scope.yamlDocument.json).toEqual({ someOtherService: [] });
      });

      it('should not call validateJson', function () {
        expect(scope.validateJson).not.toHaveBeenCalled();
      });
    });
  });

  describe('$scope.validateJson', function () {
    describe('when the yamlDocument has no/empty json', function () {
      beforeEach(function () {
        spyOn(scope, 'resetWorkspace');
        spyOn(scope, 'validateYaml');
      });

      it('resets the workspace', function () {
        scope.yamlDocument.json = {};
        scope.validateJson();
        expect(scope.resetWorkspace).toHaveBeenCalled();
      });
    });

    describe('when the yamlDocument has json', function () {
      beforeEach(function () {
        spyOn(scope, 'validateYaml');
        scope.yamlDocument.raw = null;
        scope.yamlDocument.json = {'foo': 'bar'};
        scope.validateJson();
      });

      it('stores the yamlized json into the yamlDocument.raw property', function () {
        expect(scope.yamlDocument.raw).not.toBeNull();
      });

      it('calls validatesYaml', function () {
        expect(scope.validateYaml).toHaveBeenCalled();
      });
    });
  });

  describe('deleteService event handler', function () {
    beforeEach(function () {
      spyOn(scope, 'validateJson');
    });

    describe('when the yamlDocument.json has a service matching the serviceName in the event', function () {
      beforeEach(function () {
        scope.yamlDocument.json = { someService: [] };
        scope.$emit('deleteService', 'someService');
      });

      it('should not change the yamlDocument.json', function () {
        expect(scope.yamlDocument.json).toEqual({});
      });

      it('should not call validateJson', function () {
        expect(scope.validateJson).toHaveBeenCalled();
      });
    });

    describe('when the yamlDocument.json does not have a service matching the serviceName in the event', function () {
      beforeEach(function () {
        scope.yamlDocument.json = { someOtherService: [] };
        scope.$emit('deleteService', 'someService');
      });

      it('should not change the yamlDocument.json', function () {
        expect(scope.yamlDocument.json).toEqual({ someOtherService: [] });
      });

      it('should not call validateJson', function () {
        expect(scope.validateJson).not.toHaveBeenCalled();
      });
    });
  });

});
<|endoftext|>