<commit_before>
var TEST_MAP = {
  Element_defaults: {
    init: function(){
      this.superInit();
      var PixiElement = phina.pixi.PixiElement;
      var defaults = PixiElement.defaults;
      var elm = PixiElement();
      var flag = false;
      var temp = false;
      for(var k in defaults) {
        console.assert(temp = defaults[k] === elm[k], k);
        flag = flag || !temp;
      }
      assertMessage(flag);
    },
  },

  Element_changes: {
    init: function(){
      this.superInit();
      var changeMap = {
        width: 100,
        scaleX: 5,
        scaleY: -5,
        height: 50,
        rotation: 100,
        originX: 0,
        originY: -5,
        x: 10,
        y: -100,

        top: 1,
        left: 10,
        bottom: -10,
        right: 1,

        blendMode: 'lighter',
      };

      var flag = false;
      var temp = false;
      for(var k in changeMap) {

        var elm = phina.pixi.PixiElement();
        var currentValues = {};
        for(var k2 in changeMap) {
          currentValues[k2] = elm[k2];
        }
        elm[k] = currentValues[k] = changeMap[k];
        console.assert(temp = elm[k] === currentValues[k], k + ':' + elm[k]);
        flag = flag || !temp;
      }
      assertMessage(flag);
    },
  },

  Sprite_init: {
    init:function() {
      this.superInit();
      phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
    },

    see: true,
  },

  hitTestRect: {
    init: function() {
      this.superInit();
      this.sp = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
    },

    onenter: function() {
      app._draw();
      console.assert(this.sp.hitTest(300, 400), 'hitTestRect');
      assertMessage(!this.sp.hitTest(300, 400));
    },
  },

  hitTestCircle: {
    init: function() {
      this.superInit();
      this.sp = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
      this.sp.boundingType = 'circle';
    },

    onenter: function() {
      app._draw();
      console.assert(this.sp.hitTest(300, 400), 'hitTestRect');
      assertMessage(!this.sp.hitTest(300, 400));
    },
  },

  Sprite_interactive: {
    init:function() {
      this.superInit();
      var sprite = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);

      sprite.setInteractive(true);
      sprite.fx = sprite.fy = 0;
      sprite.onpointstart = function() {
        this.fx = this.fy = 0;
        this.setScale(0.5);
      };
      sprite.onpointend = function(e) {
        this.fx = e.pointer.fx;
        this.fy = e.pointer.fy;
        this.setScale(1);
      };
      sprite.onpointmove = function(e) {
        var p = e.pointer;
        if(p.getPointing()) {
          this.x += p.dx;
          this.y += p.dy;
        }
      };
      var self = this;
      sprite.update = function() {
        var x = this.x += this.fx;
        var y = this.y += this.fy;

        if(x < 0 || x > self.width) {
          this.x = Math.clamp(x, 0, self.width);
          this.fx *= -1;
          this.scaleX *= -1;
        }

        if(y < 0 || y > self.height) {
          this.y = Math.clamp(y, 0, self.height);
          this.fy *= -1;
          this.scaleY *= -1;
        }

        this.fx *= 0.95;
        this.fy *= 0.95;
      };
    },

    controll: true,
  },

  Sprite_origin: {
    init: function (){
      this.superInit();
      var Sprite = phina.pixi.PixiSprite;
      var topleft = Sprite('tomapiko').addChildTo(this).setOrigin(0, 0);
      var bottomright = Sprite('tomapiko').addChildTo(this).setOrigin(1, 1).setPosition(this.width, this.height);
      var temp = false;
      var flag = false;
      console.assert(temp = this.width === bottomright.right, 'right');
      flag = flag || !temp;
      console.assert(temp = this.height === bottomright.bottom, 'bottom');
      flag = flag || !temp;
      console.assert(temp = 0 === topleft.top, 'top');
      flag = flag || !temp;
      console.assert(temp = 0 === topleft.left, 'left');
      assertMessage(flag);
    }
  },

  Sprite_rect: {
    init: function() {
      this.superInit();
      var Sprite = phina.pixi.PixiSprite;
      var sp = Sprite('tomapiko').addChildTo(this);
      var originStep = 0.5;
      var originStart = -5;
      var originEnd = 5;
      var xStep = this.width / 10;
      var xStart = -this.width;
      var xEnd = this.width * 2;

      var yStep = this.height / 10;
      var yStart = -this.height;
      var yEnd = this.height * 2;

      var temp = false;
      var flag = false;

      var rect = phina.geom.Rect();
      rect.width = sp.width;
      rect.height = sp.height;
      var props = 'left,right,top,bottom,centerX,centerY'.split(',');
      Array.range(originStart, originEnd, originStep).forEach(function(i) {
        sp.setOrigin(i);
        Array.range(xStart, xEnd, xStep).forEach(function(x) {
          sp.x = x;
          rect.x = x - rect.width * i;
          Array.range(yStart, yEnd, yStep).forEach(function(y){
            sp.y = y;
            rect.y = y - rect.height * i;
            props.forEach(function(p){
              // 誤差調整
              var rp = rect[p];
              var spp = sp[p];
              rp = Math.round(rp);
              spp = Math.round(spp);
              console.assert(temp = rp === spp, p + ':' + rp + ', ' + spp);
              flag = flag || !temp;
            });
          });
        });
      });

      assertMessage(flag);
      if(!flag) sp.setPosition(320, 480).setOrigin(0.5);
    }
  },

  visualProperties: {
    init: function() {
      this.superInit();
      var sp = phina.pixi.PixiSprite('tomapiko')
        .addChildTo(this)
        .setPosition(this.gridX.center(1), this.gridY.center(1));
      sp.tweener.to({
        rotation: 720,
        alpha: 0,
        width: 100,
        height: 100,
      }, 2000, 'swing').to({
        alpha: 1,
        scaleX: -2,
        scaleY: 2,
      }, 2000, 'easeInBack').to({
        rotation: 0,
      }).to({
        scaleX: 1,
        scaleY: -1
      }).setLoop(true);
    },
    see: true,
  },

  TextureFromShape: {
    init: function() {
      this.superInit();
      var shape = phina.display.StarShape();
      var texture = phina.pixi.PixiTexture.fromShape(shape);
      var sprite = phina.pixi.PixiSprite(texture).addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.tweener.to({
        rotation: 720
      });
    },

    see: true,
  },

  SpriteFromShape: {
    init: function() {
      this.superInit();
      var shape = phina.display.HeartShape();
      var sprite = phina.pixi.PixiSprite().fromShape(shape).addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.tweener.to({
        rotation: 720
      });
    },

    see: true,
  },

  SpriteFrame: {
    init: function() {
      this.superInit();
      var sprite = phina.pixi.PixiSprite('tomapiko').addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.setFrame(0, 0, sprite.width, sprite.height / 2);
      sprite.setOrigin(0.5, 1);

      var sprite2 = phina.pixi.PixiSprite('tomapiko').addChildTo(this);
      sprite2.setPosition(this.gridX.center(), this.gridY.center());
      sprite2.setFrame(0, sprite2.height / 2, sprite2.width, sprite2.height / 2);
      sprite2.setOrigin(0.5, 0);

      var self = this;

      sprite.tweener
      .by({
        y: -100,
      }, 1000, 'swing')
      .by({
        y: 100
      }, 500, 'easeInBack')
      .wait(500)
      .setLoop(true);

      sprite2.tweener
      .by({
        y: 100,
      }, 1000, 'swing')
      .by({
        y: -100
      }, 500, 'easeInBack')
      .call(function(){
        phina.pixi.PixiSprite()
        .fromShape(phina.display.StarShape())
        .addChildTo(self)
        .setPosition(sprite2.x, sprite2.y)
        .tweener.to({
          scaleX: 5,
          scaleY: 5,
          alpha: 0,
        }, 500, 'easeOutCirc').call(function() {
          this.target.remove();
        });
      })
      .wait(500)
      .setLoop(true);
    },
    see: true
  }

};

var ASSETS = {
  pixi: {
    tomapiko: 'https://raw.githubusercontent.com/phinajs/phina.js/develop/assets/images/tomapiko.png',
  }
};

if(location.href.indexOf('index.html') === -1) {
  TEST_MAP.forIn(function(className, properties){
    properties.superClass = properties.superClass || phina.pixi.PixiScene;
    phina.define('TEST_' + className, properties);
  });
  var app;
  var isLoading = true;
  phina.main(function(){
    app = phina.pixi.PixiApp({}).run().enableStats();
    app.pushScene(phina.game.LoadingScene({assets: ASSETS}).on('loaded', function(){
      isLoading = false;
      if(window !== parent) {
        post('loaded');
      }
    }));
  });

  onerror = function(e) {
    console.error(e);
    alert('エラーが発生しました。\n' + e);
  };
}
else {

  var button = document.createElement('button');
  button.textContent = 'ALL';
  button.id = 'b_ALL';
  button.style.width = '100%';
  button.onclick = function() {
    var f = document.getElementById('frame');
    var w = f.contentWindow;
    w.testAll();
  };

  document.getElementById('side').appendChild(button);
  for(var k in TEST_MAP) {
    var button = document.createElement('button');
    button.textContent = k;
    button.id = 'b_' + k;
    button.dataset.hash = k;
    button.style.width = '100%';
    if(TEST_MAP[k].see) {
      button.style.backgroundColor = 'navy';
      button.style.color = 'white';
      button.textContent = 'see: ' + k;
    }
    if(TEST_MAP[k].controll) {
      button.style.backgroundColor = 'orange';
      button.textContent = 'controll: ' + k;
    }
    button.onclick = function() {
      var f = document.getElementById('frame');
      var w = f.contentWindow;
      location.hash = w.location.hash = this.dataset.hash;
      w.replaceSceneByHash();
    };

    document.getElementById('side').appendChild(button);
  }

  onmessage = function(e) {
    var data = e.data;
    var type = data.type;
    var id = data.id;
    var b = document.getElementById('b_' + id);
    if(!b) {
      b = {style: {}, dataset: {}};
    }
    var style = b.style;

    var beforeText = '';
    var afterText = '';

    if(data === 'loaded'){
      hideLoading();
      var f = document.getElementById('frame');
      var w = f.contentWindow;
      document.getElementById('b_ALL').click();
      w.alertFlag = true;

      w.location.hash = location.hash;
      w.replaceSceneByHash();
    }
    else if (type === 'assert'){
      style.backgroundColor = data.success ? 'rgb(180, 245, 240)' : 'pink';
      afterText = data.success ? ' (success)' : '(failure)';
    }

    if(beforeText) {
      b.textContent = beforeText + b.dataset.hash;
    }

    if(afterText) {
      b.textContent = b.dataset.hash + afterText;
    }
  };

  showLoading();
  function hideLoading() {
    document.getElementById('loading').style.display = 'none';
  }
  function showLoading(){
    document.getElementById('loading').style.display = 'flex';
  }
}

function testAll(){
  console.log('全てのシーンをテストします。テストとして保証できるのは init, onenter のみです。');
  TEST_MAP.forIn(function(k, v){
    location.hash = k;
    replaceSceneByHash();
  });
}

function replaceSceneByHash(){
  if(getId() in TEST_MAP) {
    app.replaceScene(phina.using('TEST_' + getId())());
  }
}

var alertFlag = false;

function assertMessage(flag) {
  var title = getId();
  post({
    id: title,
    success: !flag,
    type: 'assert',
  });
  if(flag && alertFlag) {
    alert(title + '\nテストに失敗しました。\n開発者ツールを確認して下さい。');
  }
  else {
    console.log(location.hash + ': OK');
  }
}

function getId() {
  return location.hash.slice(1);
}

function seeMessage() {
  console.log(location.hash + ': このテストは目視で確認してください。');
  post({
    id: getId(),
    type: 'see'
  });
}
function controllMessage() {
  console.log(location.hash + ': このテストは操作して確認してください。');
  post({
    id: getId(),
    type: 'controll'
  });
}

function post(data) {
  return parent.postMessage(data, '*');
}<commit_msg>add globalize test<commit_after>
var TEST_MAP = {
  globalize: {
    init: function() {
      this.superInit();
      phina.globalize();
      phina.pixi.globalize();
      var layer = DisplayElement().addChildTo(this);
      Sprite('tomapiko').addChildTo(this)
      .setPosition(this.gridX.center(), this.gridY.center());

      Sprite(Texture.fromShape(TriangleShape())).addChildTo(layer)
      .setPosition(this.gridX.center(), this.gridY.center());
    },

    see: true,
  },

  Element_defaults: {
    init: function(){
      this.superInit();
      var PixiElement = phina.pixi.PixiElement;
      var defaults = PixiElement.defaults;
      var elm = PixiElement();
      var flag = false;
      var temp = false;
      for(var k in defaults) {
        console.assert(temp = defaults[k] === elm[k], k);
        flag = flag || !temp;
      }
      assertMessage(flag);
    },
  },

  Element_changes: {
    init: function(){
      this.superInit();
      var changeMap = {
        width: 100,
        scaleX: 5,
        scaleY: -5,
        height: 50,
        rotation: 100,
        originX: 0,
        originY: -5,
        x: 10,
        y: -100,

        top: 1,
        left: 10,
        bottom: -10,
        right: 1,

        blendMode: 'lighter',
      };

      var flag = false;
      var temp = false;
      for(var k in changeMap) {

        var elm = phina.pixi.PixiElement();
        var currentValues = {};
        for(var k2 in changeMap) {
          currentValues[k2] = elm[k2];
        }
        elm[k] = currentValues[k] = changeMap[k];
        console.assert(temp = elm[k] === currentValues[k], k + ':' + elm[k]);
        flag = flag || !temp;
      }
      assertMessage(flag);
    },
  },

  Sprite_init: {
    init:function() {
      this.superInit();
      phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
    },

    see: true,
  },

  hitTestRect: {
    init: function() {
      this.superInit();
      this.sp = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
    },

    onenter: function() {
      app._draw();
      console.assert(this.sp.hitTest(300, 400), 'hitTestRect');
      assertMessage(!this.sp.hitTest(300, 400));
    },
  },

  hitTestCircle: {
    init: function() {
      this.superInit();
      this.sp = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);
      this.sp.boundingType = 'circle';
    },

    onenter: function() {
      app._draw();
      console.assert(this.sp.hitTest(300, 400), 'hitTestRect');
      assertMessage(!this.sp.hitTest(300, 400));
    },
  },

  Sprite_interactive: {
    init:function() {
      this.superInit();
      var sprite = phina.pixi.PixiSprite('tomapiko').addChildTo(this).setPosition(300, 400);

      sprite.setInteractive(true);
      sprite.fx = sprite.fy = 0;
      sprite.onpointstart = function() {
        this.fx = this.fy = 0;
        this.setScale(0.5);
      };
      sprite.onpointend = function(e) {
        this.fx = e.pointer.fx;
        this.fy = e.pointer.fy;
        this.setScale(1);
      };
      sprite.onpointmove = function(e) {
        var p = e.pointer;
        if(p.getPointing()) {
          this.x += p.dx;
          this.y += p.dy;
        }
      };
      var self = this;
      sprite.update = function() {
        var x = this.x += this.fx;
        var y = this.y += this.fy;

        if(x < 0 || x > self.width) {
          this.x = Math.clamp(x, 0, self.width);
          this.fx *= -1;
          this.scaleX *= -1;
        }

        if(y < 0 || y > self.height) {
          this.y = Math.clamp(y, 0, self.height);
          this.fy *= -1;
          this.scaleY *= -1;
        }

        this.fx *= 0.95;
        this.fy *= 0.95;
      };
    },

    controll: true,
  },

  Sprite_origin: {
    init: function (){
      this.superInit();
      var Sprite = phina.pixi.PixiSprite;
      var topleft = Sprite('tomapiko').addChildTo(this).setOrigin(0, 0);
      var bottomright = Sprite('tomapiko').addChildTo(this).setOrigin(1, 1).setPosition(this.width, this.height);
      var temp = false;
      var flag = false;
      console.assert(temp = this.width === bottomright.right, 'right');
      flag = flag || !temp;
      console.assert(temp = this.height === bottomright.bottom, 'bottom');
      flag = flag || !temp;
      console.assert(temp = 0 === topleft.top, 'top');
      flag = flag || !temp;
      console.assert(temp = 0 === topleft.left, 'left');
      assertMessage(flag);
    }
  },

  Sprite_rect: {
    init: function() {
      this.superInit();
      var Sprite = phina.pixi.PixiSprite;
      var sp = Sprite('tomapiko').addChildTo(this);
      var originStep = 0.5;
      var originStart = -5;
      var originEnd = 5;
      var xStep = this.width / 10;
      var xStart = -this.width;
      var xEnd = this.width * 2;

      var yStep = this.height / 10;
      var yStart = -this.height;
      var yEnd = this.height * 2;

      var temp = false;
      var flag = false;

      var rect = phina.geom.Rect();
      rect.width = sp.width;
      rect.height = sp.height;
      var props = 'left,right,top,bottom,centerX,centerY'.split(',');
      Array.range(originStart, originEnd, originStep).forEach(function(i) {
        sp.setOrigin(i);
        Array.range(xStart, xEnd, xStep).forEach(function(x) {
          sp.x = x;
          rect.x = x - rect.width * i;
          Array.range(yStart, yEnd, yStep).forEach(function(y){
            sp.y = y;
            rect.y = y - rect.height * i;
            props.forEach(function(p){
              // 誤差調整
              var rp = rect[p];
              var spp = sp[p];
              rp = Math.round(rp);
              spp = Math.round(spp);
              console.assert(temp = rp === spp, p + ':' + rp + ', ' + spp);
              flag = flag || !temp;
            });
          });
        });
      });

      assertMessage(flag);
      if(!flag) sp.setPosition(320, 480).setOrigin(0.5);
    }
  },

  visualProperties: {
    init: function() {
      this.superInit();
      var sp = phina.pixi.PixiSprite('tomapiko')
        .addChildTo(this)
        .setPosition(this.gridX.center(1), this.gridY.center(1));
      sp.tweener.to({
        rotation: 720,
        alpha: 0,
        width: 100,
        height: 100,
      }, 2000, 'swing').to({
        alpha: 1,
        scaleX: -2,
        scaleY: 2,
      }, 2000, 'easeInBack').to({
        rotation: 0,
      }).to({
        scaleX: 1,
        scaleY: -1
      }).setLoop(true);
    },
    see: true,
  },

  TextureFromShape: {
    init: function() {
      this.superInit();
      var shape = phina.display.StarShape();
      var texture = phina.pixi.PixiTexture.fromShape(shape);
      var sprite = phina.pixi.PixiSprite(texture).addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.tweener.to({
        rotation: 720
      });
    },

    see: true,
  },

  SpriteFromShape: {
    init: function() {
      this.superInit();
      var shape = phina.display.HeartShape();
      var sprite = phina.pixi.PixiSprite().fromShape(shape).addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.tweener.to({
        rotation: 720
      });
    },

    see: true,
  },

  SpriteFrame: {
    init: function() {
      this.superInit();
      var sprite = phina.pixi.PixiSprite('tomapiko').addChildTo(this);
      sprite.setPosition(this.gridX.center(), this.gridY.center());
      sprite.setFrame(0, 0, sprite.width, sprite.height / 2);
      sprite.setOrigin(0.5, 1);

      var sprite2 = phina.pixi.PixiSprite('tomapiko').addChildTo(this);
      sprite2.setPosition(this.gridX.center(), this.gridY.center());
      sprite2.setFrame(0, sprite2.height / 2, sprite2.width, sprite2.height / 2);
      sprite2.setOrigin(0.5, 0);

      var self = this;

      sprite.tweener
      .by({
        y: -100,
      }, 1000, 'swing')
      .by({
        y: 100
      }, 500, 'easeInBack')
      .wait(500)
      .setLoop(true);

      sprite2.tweener
      .by({
        y: 100,
      }, 1000, 'swing')
      .by({
        y: -100
      }, 500, 'easeInBack')
      .call(function(){
        phina.pixi.PixiSprite()
        .fromShape(phina.display.StarShape())
        .addChildTo(self)
        .setPosition(sprite2.x, sprite2.y)
        .tweener.to({
          scaleX: 5,
          scaleY: 5,
          alpha: 0,
        }, 500, 'easeOutCirc').call(function() {
          this.target.remove();
        });
      })
      .wait(500)
      .setLoop(true);
    },
    see: true
  }

};

var ASSETS = {
  pixi: {
    tomapiko: 'https://raw.githubusercontent.com/phinajs/phina.js/develop/assets/images/tomapiko.png',
  }
};

if(location.href.indexOf('index.html') === -1) {
  TEST_MAP.forIn(function(className, properties){
    properties.superClass = properties.superClass || phina.pixi.PixiScene;
    phina.define('TEST_' + className, properties);
  });
  var app;
  var isLoading = true;
  phina.main(function(){
    app = phina.pixi.PixiApp({}).run().enableStats();
    app.pushScene(phina.game.LoadingScene({assets: ASSETS}).on('loaded', function(){
      isLoading = false;
      if(window !== parent) {
        post('loaded');
      }
    }));
  });

  onerror = function(e) {
    console.error(e);
    alert('エラーが発生しました。\n' + e);
  };
}
else {

  var button = document.createElement('button');
  button.textContent = 'ALL';
  button.id = 'b_ALL';
  button.style.width = '100%';
  button.onclick = function() {
    var f = document.getElementById('frame');
    var w = f.contentWindow;
    w.testAll();
  };

  document.getElementById('side').appendChild(button);
  for(var k in TEST_MAP) {
    var button = document.createElement('button');
    button.textContent = k;
    button.id = 'b_' + k;
    button.dataset.hash = k;
    button.style.width = '100%';
    if(TEST_MAP[k].see) {
      button.style.backgroundColor = 'navy';
      button.style.color = 'white';
      button.textContent = 'see: ' + k;
    }
    if(TEST_MAP[k].controll) {
      button.style.backgroundColor = 'orange';
      button.textContent = 'controll: ' + k;
    }
    button.onclick = function() {
      var f = document.getElementById('frame');
      var w = f.contentWindow;
      location.hash = w.location.hash = this.dataset.hash;
      w.replaceSceneByHash();
    };

    document.getElementById('side').appendChild(button);
  }

  onmessage = function(e) {
    var data = e.data;
    var type = data.type;
    var id = data.id;
    var b = document.getElementById('b_' + id);
    if(!b) {
      b = {style: {}, dataset: {}};
    }
    var style = b.style;

    var beforeText = '';
    var afterText = '';

    if(data === 'loaded'){
      hideLoading();
      var f = document.getElementById('frame');
      var w = f.contentWindow;
      document.getElementById('b_ALL').click();
      w.alertFlag = true;

      w.location.hash = location.hash;
      w.replaceSceneByHash();
    }
    else if (type === 'assert'){
      style.backgroundColor = data.success ? 'rgb(180, 245, 240)' : 'pink';
      afterText = data.success ? ' (success)' : '(failure)';
    }

    if(beforeText) {
      b.textContent = beforeText + b.dataset.hash;
    }

    if(afterText) {
      b.textContent = b.dataset.hash + afterText;
    }
  };

  showLoading();
  function hideLoading() {
    document.getElementById('loading').style.display = 'none';
  }
  function showLoading(){
    document.getElementById('loading').style.display = 'flex';
  }
}

function testAll(){
  console.log('全てのシーンをテストします。テストとして保証できるのは init, onenter のみです。');
  TEST_MAP.forIn(function(k, v){
    location.hash = k;
    replaceSceneByHash();
  });
}

function replaceSceneByHash(){
  if(getId() in TEST_MAP) {
    app.replaceScene(phina.using('TEST_' + getId())());
  }
}

var alertFlag = false;

function assertMessage(flag) {
  var title = getId();
  post({
    id: title,
    success: !flag,
    type: 'assert',
  });
  if(flag && alertFlag) {
    alert(title + '\nテストに失敗しました。\n開発者ツールを確認して下さい。');
  }
  else {
    console.log(location.hash + ': OK');
  }
}

function getId() {
  return location.hash.slice(1);
}

function seeMessage() {
  console.log(location.hash + ': このテストは目視で確認してください。');
  post({
    id: getId(),
    type: 'see'
  });
}
function controllMessage() {
  console.log(location.hash + ': このテストは操作して確認してください。');
  post({
    id: getId(),
    type: 'controll'
  });
}

function post(data) {
  return parent.postMessage(data, '*');
}<|endoftext|>


<commit_before>/*! JSFuck 1.0 - http://jsfuck.com */

(function(){

  var USE_CHAR_CODE = "USE_CHAR_CODE";

  var MIN = 32, MAX = 126;

  var SIMPLE = {
    'false':      '![]',
    'true':       '!![]',
    'undefined':  '[][[]]',
    'NaN':        '+[![]]',
    'Infinity':   '+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])' // +"1e1000"
  };

  var CONSTRUCTORS = {
    'Array':    '[]',
    'Number':   '+[]',
    'String':   '[]+[]',
    'Boolean':  '![]',
    'Function':  '[]["sort"]'
  };

  var MAPPING = {
    'a':   '("false")[1]',
    'b':   '(GLOBAL+[])[2]',
    'c':   '([]["filter"]+[])[3]',
    'd':   '("undefined")[2]',
    'e':   '("true")[3]',
    'f':   '("false")[0]',
    'g':   '([]+String)[14]',
    'h':   '(+(17))["toString"](18)',
    'i':   '("undefined")[5]',
    'j':   '(GLOBAL+"")[3]',
    'k':   '(+(20))["toString"](21)',
    'l':   '("false")[2]',
    'm':   '(Number+"")[11]',
    'n':   '("undefined")[1]',
    'o':   '([]["filter"]+"")[6]',
    'p':   '(+(25))["toString"](30)',
    'q':   '(+(26))["toString"](30)',
    'r':   '("true")[1]',
    's':   '("false")[3]',
    't':   '("true")[0]',
    'u':   '("undefined")[0]',
    'v':   '(+(31))["toString"](32)',
    'w':   '(+(32))["toString"](33)',
    'x':   '(+(33))["toString"](34)',
    'y':   '("Infinity")[7]',
    'z':   '(+(35))["toString"](36)',

    'A':   '(Array+"")[9]',
    'B':   '(Boolean+"")[9]',
    'C':   'GLOBAL["unescape"]("%"+(43)+"c")[0]',
    'D':   USE_CHAR_CODE,
    'E':   USE_CHAR_CODE,
    'F':   '(Function+"")[9]',
    'G':   USE_CHAR_CODE,
    'H':   USE_CHAR_CODE,
    'I':   '("Infinity")[0]',
    'J':   USE_CHAR_CODE,
    'K':   USE_CHAR_CODE,
    'L':   USE_CHAR_CODE,
    'M':   USE_CHAR_CODE,
    'N':   '("NaN")[0]',
    'O':   USE_CHAR_CODE,
    'P':   USE_CHAR_CODE,
    'Q':   USE_CHAR_CODE,
    'R':   USE_CHAR_CODE,
    'S':   '(String+[])[9]',
    'T':   USE_CHAR_CODE,
    'U':   USE_CHAR_CODE,
    'V':   USE_CHAR_CODE,
    'W':   USE_CHAR_CODE,
    'X':   USE_CHAR_CODE,
    'Y':   USE_CHAR_CODE,
    'Z':   USE_CHAR_CODE,

    ' ':   '([]["filter"]+[])[8]',
    '!':   USE_CHAR_CODE,
    '"':   '("")["link"]()[8]',
    '#':   USE_CHAR_CODE,
    '$':   USE_CHAR_CODE,
    '%':   'GLOBAL["escape"]("<")[0]',
    '&':   USE_CHAR_CODE,
    '\'':  'GLOBAL["unescape"]("%"+(27)+"c")[0]',
    '(':   '([]["filter"]+"")[15]',
    ')':   '([]["filter"]+"")[16]',
    '*':   USE_CHAR_CODE,
    '+':   USE_CHAR_CODE,
    ',':   USE_CHAR_CODE,
    '-':   USE_CHAR_CODE,
    '.':   USE_CHAR_CODE,
    '/':   '("")["sub"]()[6]',
    ':':   'GLOBAL["Date"]()[21]',
    ';':   USE_CHAR_CODE,
    '<':   '("")["sub"]()[0]',
    '=':   '("")["fontcolor"]()[11]',
    '>':   '("")["sub"]()[10]',
    '?':   USE_CHAR_CODE,
    '@':   USE_CHAR_CODE,
    '[':   USE_CHAR_CODE,
    '\\':  'GLOBAL["unescape"]("%"+(5)+"c")[0]',
    ']':   USE_CHAR_CODE,
    '^':   USE_CHAR_CODE,
    '_':   USE_CHAR_CODE,
    '`':   USE_CHAR_CODE,
    '{':   '([]["filter"]+"")[18]',
    '|':   USE_CHAR_CODE,
    '}':   USE_CHAR_CODE,
    '~':   USE_CHAR_CODE
  };

  var GLOBAL = '[]["sort"]["constructor"]("return this")()';

  function fillMissingChars(){
    for (var key in MAPPING){
      if (MAPPING[key] === USE_CHAR_CODE){
        MAPPING[key] = 'String["fromCharCode"](' + key.charCodeAt(0) + ')';
      }
    }
  }

  function fillMissingDigits(){
    var output, number, i;

    for (number = 0; number < 10; number++){

      output = "+[]";

      if (number > 0){ output = "+!" + output; }
      for (i = 1; i < number; i++){ output = "+!+[]" + output; }
      if (number > 1){ output = output.substr(1); }

      MAPPING[number] = "[" + output + "]";
    }
  }

  function replaceMap(){
    var character = "", value, original;

    function replace(pattern, replacement){
      value = value.replace(
        new RegExp(pattern, "gi"),
        replacement
      );
    }

    for (var i = MIN; i <= MAX; i++){
      character = String.fromCharCode(i);
      value = MAPPING[character];
      original = value;

      for (key in CONSTRUCTORS){
        replace("\\b" + key, '(' + CONSTRUCTORS[key] + ')["constructor"]');
      }

      for (key in SIMPLE){
        replace('"' + key + '"', SIMPLE[key] + "+[]");
        replace(key, SIMPLE[key]);
      }

      for (key = 0; key < 10; key++){
        replace(key, "+[" + MAPPING[key] + "]");
      }

      replace("GLOBAL", GLOBAL);
      replace('\\+""', "+[]");
      replace('""', "[]+[]");

      MAPPING[character] = value;
    }
  }

  function replaceStrings(){
    var regEx = /[^\[\]\(\)\!\+]{1}/g,
      all, value, missing;

      count = MAX - MIN;

    function findMissing(){
      var all, value, done = false;

      missing = {};

      for (all in MAPPING){

        value = MAPPING[all];

        if (value.match(regEx)){
          missing[all] = value;
          done = true;
        }
      }

      return done;
    }

    for (all in MAPPING){
      MAPPING[all] = MAPPING[all].replace(/\"([^\"]+)\"/gi, function(a, b){
        return b.split("").join("+");
      });
    }

    while (findMissing()){
      for (all in missing){
        value = MAPPING[all];
        value = value.replace(regEx, function(c){
          return missing[c] ? c : MAPPING[c];
        });

        MAPPING[all] = value;
        missing[all] = value;
      }

      if (count-- == 0){
        console.error("Could not compile the following chars:", missing);
      }
    }
  }

  function encode(input, wrapWithEval){
    var output = [];

    if (!input){
      return "";
    }

    input.replace(/./g, function(c){
      output.push(MAPPING[c]);
    });

    output = output.join("+");

    if (/^\d$/.test(input)){
      output += "+[]";
    }

    if (wrapWithEval){
      output = "[][" + encode("filter") + "]" +
        "[" + encode("constructor") + "]" +
        "(" + output + ")()"
    }

    return output;
  }

  fillMissingDigits();
  fillMissingChars();
  replaceMap();
  replaceStrings();

  this.JSFuck = {
    encode: encode
  };
})();<commit_msg>Minimize generated code<commit_after>/*! JSFuck 1.0 - http://jsfuck.com */

(function(){

  var USE_CHAR_CODE = "USE_CHAR_CODE";

  var MIN = 32, MAX = 126;

  var SIMPLE = {
    'false':      '![]',
    'true':       '!![]',
    'undefined':  '[][[]]',
    'NaN':        '+[![]]',
    'Infinity':   '+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]]+[+[]])' // +"1e1000"
  };

  var CONSTRUCTORS = {
    'Array':    '[]',
    'Number':   '+[]',
    'String':   '[]+[]',
    'Boolean':  '![]',
    'Function':  '[]["filter"]'
  };

  var MAPPING = {
    'a':   '("false")[1]',
    'b':   '(GLOBAL+[])[2]',
    'c':   '([]["filter"]+[])[3]',
    'd':   '("undefined")[2]',
    'e':   '("true")[3]',
    'f':   '("false")[0]',
    'g':   '([]+String)[14]',
    'h':   '(+(17))["toString"](20)',
    'i':   '("undefined")[5]',
    'j':   '(GLOBAL+"")[3]',
    'k':   '(+(20))["toString"](21)',
    'l':   '("false")[2]',
    'm':   '(Number+"")[11]',
    'n':   '("undefined")[1]',
    'o':   '([]["filter"]+"")[6]',
    'p':   '(+(25))["toString"](30)',
    'q':   '(+(26))["toString"](30)',
    'r':   '("true")[1]',
    's':   '("false")[3]',
    't':   '("true")[0]',
    'u':   '("undefined")[0]',
    'v':   '(+(31))["toString"](32)',
    'w':   '(+(32))["toString"](33)',
    'x':   '(+(33))["toString"](34)',
    'y':   '("Infinity")[7]',
    'z':   '(+(35))["toString"](36)',

    'A':   '(Array+"")[9]',
    'B':   '(Boolean+"")[9]',
    'C':   'GLOBAL["unescape"]("%"+(43))[0]',
    'D':   USE_CHAR_CODE,
    'E':   USE_CHAR_CODE,
    'F':   '(Function+"")[9]',
    'G':   USE_CHAR_CODE,
    'H':   USE_CHAR_CODE,
    'I':   '("Infinity")[0]',
    'J':   USE_CHAR_CODE,
    'K':   USE_CHAR_CODE,
    'L':   USE_CHAR_CODE,
    'M':   USE_CHAR_CODE,
    'N':   '("NaN")[0]',
    'O':   USE_CHAR_CODE,
    'P':   USE_CHAR_CODE,
    'Q':   USE_CHAR_CODE,
    'R':   USE_CHAR_CODE,
    'S':   '(String+[])[9]',
    'T':   USE_CHAR_CODE,
    'U':   USE_CHAR_CODE,
    'V':   USE_CHAR_CODE,
    'W':   USE_CHAR_CODE,
    'X':   USE_CHAR_CODE,
    'Y':   USE_CHAR_CODE,
    'Z':   USE_CHAR_CODE,

    ' ':   '([]["filter"]+[])[8]',
    '!':   USE_CHAR_CODE,
    '"':   '("")["link"](0)[10]',
    '#':   USE_CHAR_CODE,
    '$':   USE_CHAR_CODE,
    '%':   'GLOBAL["escape"]("<")[0]',
    '&':   USE_CHAR_CODE,
    '\'':  'GLOBAL["unescape"]("%"+(27))[0]',
    '(':   '([]["filter"]+"")[15]',
    ')':   '([]["filter"]+"")[16]',
    '*':   USE_CHAR_CODE,
    '+':   '(+(+!+[]+(!+[]+[])[!+[]+!+[]+!+[]]+[+!+[]]+[+[]]+[+[]])+[])[2]',
    ',':   '[[]]["concat"]([][[]])+[]',
    '-':   USE_CHAR_CODE,
    '.':   USE_CHAR_CODE,
    '/':   '("")["link"]()[21]',
    ':':   'GLOBAL["Date"]()[21]',
    ';':   USE_CHAR_CODE,
    '<':   '("")["link"]()[0]',
    '=':   '("")["fontcolor"]()[11]',
    '>':   '("")["link"]([])[10]',
    '?':   USE_CHAR_CODE,
    '@':   USE_CHAR_CODE,
    '[':   USE_CHAR_CODE,
    '\\':  'GLOBAL["unescape"]("%"+(5)+"c")[0]',
    ']':   USE_CHAR_CODE,
    '^':   USE_CHAR_CODE,
    '_':   USE_CHAR_CODE,
    '`':   USE_CHAR_CODE,
    '{':   '([]["filter"]+"")[18]',
    '|':   USE_CHAR_CODE,
    '}':   USE_CHAR_CODE,
    '~':   USE_CHAR_CODE
  };

  var GLOBAL = '[]["filter"]["constructor"]("return this")()';

  function fillMissingChars(){
    for (var key in MAPPING){
      if (MAPPING[key] === USE_CHAR_CODE){
        MAPPING[key] = 'String["fromCharCode"](' + key.charCodeAt(0) + ')';
      }
    }
  }

  function fillMissingDigits(){
    var output, number, i;

    for (number = 0; number < 10; number++){

      output = "+[]";

      if (number > 0){ output = "+!" + output; }
      for (i = 1; i < number; i++){ output = "+!+[]" + output; }
      if (number > 1){ output = output.substr(1); }

      MAPPING[number] = "[" + output + "]";
    }
  }

  function replaceMap(){
    var character = "", value, original;

    function replace(pattern, replacement){
      value = value.replace(
        new RegExp(pattern, "gi"),
        replacement
      );
    }

    for (var i = MIN; i <= MAX; i++){
      character = String.fromCharCode(i);
      value = MAPPING[character];
      original = value;

      for (key in CONSTRUCTORS){
        replace("\\b" + key, '(' + CONSTRUCTORS[key] + ')["constructor"]');
      }

      for (key in SIMPLE){
        replace('"' + key + '"', SIMPLE[key] + "+[]");
        replace(key, SIMPLE[key]);
      }

      for (key = 0; key < 10; key++){
        replace(key, "+[" + MAPPING[key] + "]");
      }

      replace("GLOBAL", GLOBAL);
      replace('\\+""', "+[]");
      replace('""', "[]+[]");

      MAPPING[character] = value;
    }
  }

  function replaceStrings(){
    var regEx = /[^\[\]\(\)\!\+]{1}/g,
      all, value, missing;

      count = MAX - MIN;

    function findMissing(){
      var all, value, done = false;

      missing = {};

      for (all in MAPPING){

        value = MAPPING[all];

        if (value.match(regEx)){
          missing[all] = value;
          done = true;
        }
      }

      return done;
    }

    for (all in MAPPING){
      MAPPING[all] = MAPPING[all].replace(/\"([^\"]+)\"/gi, function(a, b){
        return b.split("").join("+");
      });
    }

    while (findMissing()){
      for (all in missing){
        value = MAPPING[all];
        value = value.replace(regEx, function(c){
          return missing[c] ? c : MAPPING[c];
        });

        MAPPING[all] = value;
        missing[all] = value;
      }

      if (count-- == 0){
        console.error("Could not compile the following chars:", missing);
      }
    }
  }

  function encode(input, wrapWithEval){
    var output = [];

    if (!input){
      return "";
    }

    input.replace(/./g, function(c){
      output.push(MAPPING[c]);
    });

    output = output.join("+");

    if (/^\d$/.test(input)){
      output += "+[]";
    }

    if (wrapWithEval){
      output = "[][" + encode("filter") + "]" +
        "[" + encode("constructor") + "]" +
        "(" + output + ")()"
    }

    return output;
  }

  fillMissingDigits();
  fillMissingChars();
  replaceMap();
  replaceStrings();

  this.JSFuck = {
    encode: encode
  };
})();<|endoftext|>


<commit_before>
const fs = require('fs')

const moo = require('../moo')
const compile = moo.compile
const python = require('./python')

describe('moo compiler', () => {

  test("warns for /g, /y, /i, /m", () => {
    expect(() => compile({ word: /foo/ })).not.toThrow()
    expect(() => compile({ word: /foo/g })).toThrow()
    expect(() => compile({ word: /foo/i })).toThrow()
    expect(() => compile({ word: /foo/y })).toThrow()
    expect(() => compile({ word: /foo/m })).toThrow()
  })

  // TODO warns for multiple capture groups

  // TODO wraps zero capture groups

  // TODO warns if no lineBreaks: true

  test('sorts regexps and strings', () => {
    let lexer = moo.compile({
      tok: [/t[ok]+/, /\w/, 'tok', 'token']
    })
    expect(lexer.re.source.replace(/[(?:)]/g, '')).toBe('token|tok|t[ok]+|\\w')
  })

  test('warns about missing states', () => {
    const rules = [
      {match: '=', next: 'missing'},
      {match: '=', push: 'missing'},
    ]
    for (const rule of rules) {
      expect(() => moo.states({start: {thing: rule}}))
      .toThrow("Missing state 'missing' (in token 'thing' of state 'start')")
    }
  })

  test('warns about inappropriate state-switching options', () => {
    const rules = [
      {match: '=', next: 'state'},
      {match: '=', push: 'state'},
      {match: '=', pop: true},
    ]
    for (const rule of rules) {
      expect(() => moo.compile({thing: rule}))
      .toThrow("State-switching options are not allowed in stateless lexers (for token 'thing')")
    }
  })

})

describe('moo lexer', () => {

  var simpleLexer = compile({
    word: /[a-z]+/,
    number: /[0-9]+/,
    ws: / +/,
  })

  test('vaguely works', () => {
    simpleLexer.reset('ducks are 123 bad')
    expect(simpleLexer.lex()).toMatchObject({ type: 'word', value: 'ducks' })
    expect(simpleLexer.lex()).toMatchObject({ type: 'ws', value: ' ' })
    expect(simpleLexer.lex()).toMatchObject({ type: 'word', value: 'are' })
  })

  test('accepts rules in an object', () => {
    const lexer = compile({
      word: /[a-z]+/,
      number: /[0-9]+/,
      space: / +/,
    })
    lexer.reset('ducks are 123 bad')
    expect(lexer.lex()).toMatchObject({type: 'word', value: 'ducks'})
    expect(lexer.lex()).toMatchObject({type: 'space', value: ' '})
  })

  test('accepts a list of regexps', () => {
    const lexer = compile({
      number: [
        /[0-9]+\.[0-9]+/,
        /[0-9]+/,
      ],
      space: / +/,
    })
    lexer.reset('12.04 123 3.14')
    var tokens = lexer.lexAll().filter(t => t.type !== 'space')
    expect(tokens.shift()).toMatchObject({type: 'number', value: '12.04'})
    expect(tokens.shift()).toMatchObject({type: 'number', value: '123'})
    expect(tokens.shift()).toMatchObject({type: 'number', value: '3.14'})
  })

  test('no capture groups', () => {
    let lexer = compile({
      a: /a+/,
      b: /b|c/,
    })
    lexer.reset('aaaaabcbcbcbc')
    expect(lexer.lex().value).toEqual('aaaaa')
    expect(lexer.lex().value).toEqual('b')
    expect(lexer.lex().value).toEqual('c')
    expect(lexer.lex().value).toEqual('b')
  })

  test('multiline', () => {
    var lexer = compile({
      file: { match: /([^]+)/, lineBreaks: true },
    }).reset('I like to moo\na lot')
    expect(lexer.lex().value).toBe('I like to moo\na lot')
  })

  test('match EOL $', () => {
    var lexer = compile({
      x_eol: /x$/,
      x: /x/,
      WS: / +/,
      NL: { match: /\n/, lineBreaks: true },
      other: /[^ \n]+/,
    }).reset('x \n x\n yz x')
    let tokens = lexer.lexAll().filter(t => t.type !== 'WS')
    expect(tokens.map(t => [t.type, t.value])).toEqual([
      ['x', 'x'],
      ['NL', '\n'],
      ['x_eol', 'x'],
      ['NL', '\n'],
      ['other', 'yz'],
      ['x_eol', 'x'],
    ])
  })

  test('match BOL ^', () => {
    var lexer = compile({
      x_bol: /^x/,
      x: /x/,
      WS: / +/,
      NL: { match: /\n/, lineBreaks: true },
      other: /[^ \n]+/,
    }).reset('x \n x\nx yz')
    let tokens = lexer.lexAll().filter(t => t.type !== 'WS')
    expect(tokens.map(t => [t.type, t.value])).toEqual([
      ['x_bol', 'x'],
      ['NL', '\n'],
      ['x', 'x'],
      ['NL', '\n'],
      ['x_bol', 'x'],
      ['other', 'yz'],
    ])
  })

  test('token to string conversion', () => {
    const lexer = compile({
      apples: /()a/,
      pears: /p/,
    }).reset('ap')
    expect(String(lexer.lex())).toBe('apples')
    expect(String(lexer.lex())).toBe('p')
  })

  // TODO test / design API for errors
  // - check the reported error location

  test('kurt tokens', () => {
    let pythonLexer = compile(python.rules)
    let tokens = pythonLexer.reset(fs.readFileSync('test/kurt.py', 'utf-8')).lexAll()
    expect(tokens.length).toBe(14513)
  })

  // TODO test clone()
})

describe('moo stateful lexer', () => {

  test('switches states', () => {
    const lexer = moo.states({
      start: {
        word: /\w+/,
        eq: {match: '=', next: 'ab'},
      },
      ab: {
        a: 'a',
        b: 'b',
        semi: {match: ';', next: 'start'},
      },
    })

    lexer.reset('one=ab;two=')
    expect(lexer.lexAll().map(({type, value}) => [type, value])).toEqual([
      ['word', 'one'],
      ['eq', '='],
      ['a', 'a'],
      ['b', 'b'],
      ['semi', ';'],
      ['word', 'two'],
      ['eq', '='],
    ])
  })

  const parens = moo.states({
    start: {
      word: /\w+/,
      lpar: {match: '(', push: 'inner'},
      rpar: ')',
    },
    inner: {
      thing: /\w+/,
      lpar: {match: '(', push: 'inner'},
      rpar: {match: ')', pop: true},
    },
  })

  test('maintains a stack', () => {
    parens.reset('a(b(c)d)e')
    expect(parens.lexAll().map(({type, value}) => [type, value])).toEqual([
      ['word', 'a'],
      ['lpar', '('],
      ['thing', 'b'],
      ['lpar', '('],
      ['thing', 'c'],
      ['rpar', ')'],
      ['thing', 'd'],
      ['rpar', ')'],
      ['word', 'e'],
    ])
  })

  test('allows popping too many times', () => {
    parens.reset(')e')
    expect(parens.lexAll().map(({type, value}) => [type, value])).toEqual([
      ['rpar', ')'],
      ['word', 'e'],
    ])
  })

  test('lexes interpolation example', () => {
    let lexer = moo.states({
      main: {
        strstart: {match: '`', push: 'lit'},
        ident:    /\w+/,
        lbrace:   {match: '{', push: 'main'},
        rbrace:   {match: '}', pop: 1},
        colon:    ':',
        space:    {match: /\s+/, lineBreaks: true},
      },
      lit: {
        interp:   {match: '${', push: 'main'},
        escape:   /\\./,
        strend:   {match: '`', pop: 1},
        const:    {match: /(?:[^$`]|\$(?!\{))+/, lineBreaks: true},
      },
    }).feed('`a${{c: d}}e`')
    expect(lexer.lexAll().map(t => t.type).join(' ')).toBe('strstart const interp lbrace ident colon space ident rbrace rbrace const strend')
  })

})

describe('line numbers', () => {

  var testLexer = compile({
    WS: / +/,
    word: /[a-z]+/,
    NL: { match: /\n/, lineBreaks: true },
  })

  test('counts line numbers', () => {
    var tokens = testLexer.reset('cow\nfarm\ngrass').lexAll()
    expect(tokens.map(t => t.value)).toEqual(['cow', '\n', 'farm', '\n', 'grass'])
    expect(tokens.map(t => t.lineBreaks)).toEqual([0, 1, 0, 1, 0])
    expect(tokens.map(t => t.size)).toEqual([3, 1, 4, 1, 5])
    expect(tokens.map(t => t.line)).toEqual([1, 1, 2, 2, 3])
    expect(tokens.map(t => t.col)).toEqual([1, 4, 1, 5, 1])
  })

  test('tracks columns', () => {
    var lexer = compile({
      WS: / +/,
      thing: { match: /[a-z\n]+/, lineBreaks: true },
    })
    lexer.reset('pie cheese\nsalad what\n ')
    expect(lexer.lex()).toMatchObject({ value: 'pie', col: 1 })
    expect(lexer.lex()).toMatchObject({ value: ' ', col: 4 })
    expect(lexer.lex()).toMatchObject({ value: 'cheese\nsalad', col: 5, line: 1 })
    expect(lexer.lex()).toMatchObject({ value: ' ', col: 6, line: 2 })
    expect(lexer.lex()).toMatchObject({ value: 'what\n', col: 7, line: 2 })
    expect(lexer.lex()).toMatchObject({ value: ' ', col: 1, line: 3 })
  })

  test('tries to warn if rule matches \\n', () => {
    expect(() => compile([['whitespace', /\s+/]])).toThrow()
    expect(() => compile([['multiline', /q[^]*/]])).not.toThrow()
  })

  test('resets state', () => {
    var lexer = compile({
      WS: / +/,
      word: /[a-z]+/,
    })
    lexer.reset('potatoes\nsalad')
    expect(lexer).toMatchObject({buffer: 'potatoes\nsalad', line: 1, col: 1})
    lexer.lexAll()
    expect(lexer).toMatchObject({line: 2, col: 6})
    lexer.reset('cheesecake')
    expect(lexer).toMatchObject({buffer: 'cheesecake', line: 1, col: 1})
  })

  // TODO test clone()
})

describe('save/restore', () => {

  const testLexer = compile({
    word: /[a-z]+/,
    NL: { match: '\n', lineBreaks: true },
  })

  test('can be saved', () => {
    testLexer.reset('one\ntwo')
    testLexer.lexAll()
    expect(testLexer.save()).toEqual({line: 2, col: 4})
  })

  test('can be restored', () => {
    testLexer.reset('\nthree', {line: 2, col: 4})
    expect(testLexer).toMatchObject({line: 2, col: 4, buffer: '\nthree'})
  })

})

describe('python tokenizer', () => {

  test("1 + 2", () => {
    expect(python.outputTokens("1 + 2")).toEqual([
      'NUMBER "1"',
      'OP "+"',
      'NUMBER "2"',
      'ENDMARKER ""',
    ])
  })

  // use non-greedy matching
  test('triple-quoted strings', () => {
    let example = '"""abc""" 1+1 """def"""'
    let pythonLexer = compile(python.rules)
    expect(pythonLexer.reset(example).lexAll().map(t => t.value)).toEqual(
      ['"""abc"""', " ", "1", "+", "1", " ", '"""def"""']
    )
  })

  test('example python file', () => {
    expect(python.outputTokens(python.pythonFile)).toEqual([
      // 'ENCODING "utf-8"',
      'COMMENT "#!/usr/local/bin/python3"',
      'NL "\\n"',
      'NAME "import"',
      'NAME "sys"',
      'NEWLINE "\\n"',
      'NAME "from"',
      'NAME "tokenize"',
      'NAME "import"',
      'NAME "tokenize"',
      'OP ","',
      'NAME "tok_name"',
      'NEWLINE "\\n"',
      'NAME "import"',
      'NAME "json"',
      'NEWLINE "\\n"',
      'NAME "from"',
      'NAME "io"',
      'NAME "import"',
      'NAME "BytesIO"',
      'NEWLINE "\\n"',
      'NL "\\n"',
      'NAME "path"',
      'OP "="',
      'NAME "sys"',
      'OP "."',
      'NAME "argv"',
      'OP "["',
      'NUMBER "1"',
      'OP "]"',
      'NEWLINE "\\n"',
      'NAME "for"',
      'NAME "info"',
      'NAME "in"',
      'NAME "tokenize"',
      'OP "("',
      'NAME "open"',
      'OP "("',
      'NAME "path"',
      'OP ","',
      'STRING "rb"',
      'OP ")"',
      'OP "."',
      'NAME "readline"',
      'OP ")"',
      'OP ":"',
      'NEWLINE "\\n"',
      'INDENT "    "',
      'NAME "print"',
      'OP "("',
      'NAME "tok_name"',
      'OP "["',
      'NAME "info"',
      'OP "."',
      'NAME "type"',
      'OP "]"',
      'OP ","',
      'NAME "json"',
      'OP "."',
      'NAME "dumps"',
      'OP "("',
      'NAME "info"',
      'OP "."',
      'NAME "string"',
      'OP ")"',
      'OP ")"',
      'NEWLINE "\\n"',
      // 'NL "\\n"',
      'DEDENT ""',
      'ENDMARKER ""',
    ])
  })

  test("kurt python", () => {
    let tokens = python.outputTokens(fs.readFileSync('test/kurt.py', 'utf-8'))
    expect(tokens[100]).toBe('NAME "def"')
    expect(tokens.pop()).toBe('ENDMARKER ""')
    tokens.pop()
    expect(tokens.pop()).not.toBe('ERRORTOKEN ""')
    expect(tokens.length).toBe(11616)

    // let expected = fs.readFileSync('test/kurt-tokens.txt', 'utf-8').split('\n')
    // expect(tokens).toEqual(expected)
  })

})

describe('tosh tokenizer', () => {

  const tosh = require('./tosh')

  test('tosh', () => {
    let oldTokens = tosh.oldTokenizer(tosh.exampleFile)
    expect(tosh.tokenize(tosh.exampleFile)).toEqual(oldTokens)
  })

})
<commit_msg>Test multiple capturing RegExps<commit_after>
const fs = require('fs')

const moo = require('../moo')
const compile = moo.compile
const python = require('./python')

describe('moo compiler', () => {

  test("warns for /g, /y, /i, /m", () => {
    expect(() => compile({ word: /foo/ })).not.toThrow()
    expect(() => compile({ word: /foo/g })).toThrow()
    expect(() => compile({ word: /foo/i })).toThrow()
    expect(() => compile({ word: /foo/y })).toThrow()
    expect(() => compile({ word: /foo/m })).toThrow()
  })

  // TODO warns for multiple capture groups

  // TODO wraps zero capture groups

  // TODO warns if no lineBreaks: true

  test('sorts regexps and strings', () => {
    let lexer = moo.compile({
      tok: [/t[ok]+/, /\w/, 'tok', 'token']
    })
    expect(lexer.re.source.replace(/[(?:)]/g, '')).toBe('token|tok|t[ok]+|\\w')
  })

  test('allows list of RegExps', () => {
    expect(() => moo.compile({
      tok: [/(foo)/, /(bar)/]
    })).not.toThrow()
  })

  test('warns about missing states', () => {
    const rules = [
      {match: '=', next: 'missing'},
      {match: '=', push: 'missing'},
    ]
    for (const rule of rules) {
      expect(() => moo.states({start: {thing: rule}}))
      .toThrow("Missing state 'missing' (in token 'thing' of state 'start')")
    }
  })

  test('warns about inappropriate state-switching options', () => {
    const rules = [
      {match: '=', next: 'state'},
      {match: '=', push: 'state'},
      {match: '=', pop: true},
    ]
    for (const rule of rules) {
      expect(() => moo.compile({thing: rule}))
      .toThrow("State-switching options are not allowed in stateless lexers (for token 'thing')")
    }
  })

})

describe('moo lexer', () => {

  var simpleLexer = compile({
    word: /[a-z]+/,
    number: /[0-9]+/,
    ws: / +/,
  })

  test('vaguely works', () => {
    simpleLexer.reset('ducks are 123 bad')
    expect(simpleLexer.lex()).toMatchObject({ type: 'word', value: 'ducks' })
    expect(simpleLexer.lex()).toMatchObject({ type: 'ws', value: ' ' })
    expect(simpleLexer.lex()).toMatchObject({ type: 'word', value: 'are' })
  })

  test('accepts rules in an object', () => {
    const lexer = compile({
      word: /[a-z]+/,
      number: /[0-9]+/,
      space: / +/,
    })
    lexer.reset('ducks are 123 bad')
    expect(lexer.lex()).toMatchObject({type: 'word', value: 'ducks'})
    expect(lexer.lex()).toMatchObject({type: 'space', value: ' '})
  })

  test('accepts a list of regexps', () => {
    const lexer = compile({
      number: [
        /[0-9]+\.[0-9]+/,
        /[0-9]+/,
      ],
      space: / +/,
    })
    lexer.reset('12.04 123 3.14')
    var tokens = lexer.lexAll().filter(t => t.type !== 'space')
    expect(tokens.shift()).toMatchObject({type: 'number', value: '12.04'})
    expect(tokens.shift()).toMatchObject({type: 'number', value: '123'})
    expect(tokens.shift()).toMatchObject({type: 'number', value: '3.14'})
  })

  test('no capture groups', () => {
    let lexer = compile({
      a: /a+/,
      b: /b|c/,
    })
    lexer.reset('aaaaabcbcbcbc')
    expect(lexer.lex().value).toEqual('aaaaa')
    expect(lexer.lex().value).toEqual('b')
    expect(lexer.lex().value).toEqual('c')
    expect(lexer.lex().value).toEqual('b')
  })

  test('multiline', () => {
    var lexer = compile({
      file: { match: /([^]+)/, lineBreaks: true },
    }).reset('I like to moo\na lot')
    expect(lexer.lex().value).toBe('I like to moo\na lot')
  })

  test('match EOL $', () => {
    var lexer = compile({
      x_eol: /x$/,
      x: /x/,
      WS: / +/,
      NL: { match: /\n/, lineBreaks: true },
      other: /[^ \n]+/,
    }).reset('x \n x\n yz x')
    let tokens = lexer.lexAll().filter(t => t.type !== 'WS')
    expect(tokens.map(t => [t.type, t.value])).toEqual([
      ['x', 'x'],
      ['NL', '\n'],
      ['x_eol', 'x'],
      ['NL', '\n'],
      ['other', 'yz'],
      ['x_eol', 'x'],
    ])
  })

  test('match BOL ^', () => {
    var lexer = compile({
      x_bol: /^x/,
      x: /x/,
      WS: / +/,
      NL: { match: /\n/, lineBreaks: true },
      other: /[^ \n]+/,
    }).reset('x \n x\nx yz')
    let tokens = lexer.lexAll().filter(t => t.type !== 'WS')
    expect(tokens.map(t => [t.type, t.value])).toEqual([
      ['x_bol', 'x'],
      ['NL', '\n'],
      ['x', 'x'],
      ['NL', '\n'],
      ['x_bol', 'x'],
      ['other', 'yz'],
    ])
  })

  test('token to string conversion', () => {
    const lexer = compile({
      apples: /()a/,
      pears: /p/,
    }).reset('ap')
    expect(String(lexer.lex())).toBe('apples')
    expect(String(lexer.lex())).toBe('p')
  })

  // TODO test / design API for errors
  // - check the reported error location

  test('kurt tokens', () => {
    let pythonLexer = compile(python.rules)
    let tokens = pythonLexer.reset(fs.readFileSync('test/kurt.py', 'utf-8')).lexAll()
    expect(tokens.length).toBe(14513)
  })

  // TODO test clone()
})

describe('moo stateful lexer', () => {

  test('switches states', () => {
    const lexer = moo.states({
      start: {
        word: /\w+/,
        eq: {match: '=', next: 'ab'},
      },
      ab: {
        a: 'a',
        b: 'b',
        semi: {match: ';', next: 'start'},
      },
    })

    lexer.reset('one=ab;two=')
    expect(lexer.lexAll().map(({type, value}) => [type, value])).toEqual([
      ['word', 'one'],
      ['eq', '='],
      ['a', 'a'],
      ['b', 'b'],
      ['semi', ';'],
      ['word', 'two'],
      ['eq', '='],
    ])
  })

  const parens = moo.states({
    start: {
      word: /\w+/,
      lpar: {match: '(', push: 'inner'},
      rpar: ')',
    },
    inner: {
      thing: /\w+/,
      lpar: {match: '(', push: 'inner'},
      rpar: {match: ')', pop: true},
    },
  })

  test('maintains a stack', () => {
    parens.reset('a(b(c)d)e')
    expect(parens.lexAll().map(({type, value}) => [type, value])).toEqual([
      ['word', 'a'],
      ['lpar', '('],
      ['thing', 'b'],
      ['lpar', '('],
      ['thing', 'c'],
      ['rpar', ')'],
      ['thing', 'd'],
      ['rpar', ')'],
      ['word', 'e'],
    ])
  })

  test('allows popping too many times', () => {
    parens.reset(')e')
    expect(parens.lexAll().map(({type, value}) => [type, value])).toEqual([
      ['rpar', ')'],
      ['word', 'e'],
    ])
  })

  test('lexes interpolation example', () => {
    let lexer = moo.states({
      main: {
        strstart: {match: '`', push: 'lit'},
        ident:    /\w+/,
        lbrace:   {match: '{', push: 'main'},
        rbrace:   {match: '}', pop: 1},
        colon:    ':',
        space:    {match: /\s+/, lineBreaks: true},
      },
      lit: {
        interp:   {match: '${', push: 'main'},
        escape:   /\\./,
        strend:   {match: '`', pop: 1},
        const:    {match: /(?:[^$`]|\$(?!\{))+/, lineBreaks: true},
      },
    }).feed('`a${{c: d}}e`')
    expect(lexer.lexAll().map(t => t.type).join(' ')).toBe('strstart const interp lbrace ident colon space ident rbrace rbrace const strend')
  })

})

describe('line numbers', () => {

  var testLexer = compile({
    WS: / +/,
    word: /[a-z]+/,
    NL: { match: /\n/, lineBreaks: true },
  })

  test('counts line numbers', () => {
    var tokens = testLexer.reset('cow\nfarm\ngrass').lexAll()
    expect(tokens.map(t => t.value)).toEqual(['cow', '\n', 'farm', '\n', 'grass'])
    expect(tokens.map(t => t.lineBreaks)).toEqual([0, 1, 0, 1, 0])
    expect(tokens.map(t => t.size)).toEqual([3, 1, 4, 1, 5])
    expect(tokens.map(t => t.line)).toEqual([1, 1, 2, 2, 3])
    expect(tokens.map(t => t.col)).toEqual([1, 4, 1, 5, 1])
  })

  test('tracks columns', () => {
    var lexer = compile({
      WS: / +/,
      thing: { match: /[a-z\n]+/, lineBreaks: true },
    })
    lexer.reset('pie cheese\nsalad what\n ')
    expect(lexer.lex()).toMatchObject({ value: 'pie', col: 1 })
    expect(lexer.lex()).toMatchObject({ value: ' ', col: 4 })
    expect(lexer.lex()).toMatchObject({ value: 'cheese\nsalad', col: 5, line: 1 })
    expect(lexer.lex()).toMatchObject({ value: ' ', col: 6, line: 2 })
    expect(lexer.lex()).toMatchObject({ value: 'what\n', col: 7, line: 2 })
    expect(lexer.lex()).toMatchObject({ value: ' ', col: 1, line: 3 })
  })

  test('tries to warn if rule matches \\n', () => {
    expect(() => compile([['whitespace', /\s+/]])).toThrow()
    expect(() => compile([['multiline', /q[^]*/]])).not.toThrow()
  })

  test('resets state', () => {
    var lexer = compile({
      WS: / +/,
      word: /[a-z]+/,
    })
    lexer.reset('potatoes\nsalad')
    expect(lexer).toMatchObject({buffer: 'potatoes\nsalad', line: 1, col: 1})
    lexer.lexAll()
    expect(lexer).toMatchObject({line: 2, col: 6})
    lexer.reset('cheesecake')
    expect(lexer).toMatchObject({buffer: 'cheesecake', line: 1, col: 1})
  })

  // TODO test clone()
})

describe('save/restore', () => {

  const testLexer = compile({
    word: /[a-z]+/,
    NL: { match: '\n', lineBreaks: true },
  })

  test('can be saved', () => {
    testLexer.reset('one\ntwo')
    testLexer.lexAll()
    expect(testLexer.save()).toEqual({line: 2, col: 4})
  })

  test('can be restored', () => {
    testLexer.reset('\nthree', {line: 2, col: 4})
    expect(testLexer).toMatchObject({line: 2, col: 4, buffer: '\nthree'})
  })

})

describe('python tokenizer', () => {

  test("1 + 2", () => {
    expect(python.outputTokens("1 + 2")).toEqual([
      'NUMBER "1"',
      'OP "+"',
      'NUMBER "2"',
      'ENDMARKER ""',
    ])
  })

  // use non-greedy matching
  test('triple-quoted strings', () => {
    let example = '"""abc""" 1+1 """def"""'
    let pythonLexer = compile(python.rules)
    expect(pythonLexer.reset(example).lexAll().map(t => t.value)).toEqual(
      ['"""abc"""', " ", "1", "+", "1", " ", '"""def"""']
    )
  })

  test('example python file', () => {
    expect(python.outputTokens(python.pythonFile)).toEqual([
      // 'ENCODING "utf-8"',
      'COMMENT "#!/usr/local/bin/python3"',
      'NL "\\n"',
      'NAME "import"',
      'NAME "sys"',
      'NEWLINE "\\n"',
      'NAME "from"',
      'NAME "tokenize"',
      'NAME "import"',
      'NAME "tokenize"',
      'OP ","',
      'NAME "tok_name"',
      'NEWLINE "\\n"',
      'NAME "import"',
      'NAME "json"',
      'NEWLINE "\\n"',
      'NAME "from"',
      'NAME "io"',
      'NAME "import"',
      'NAME "BytesIO"',
      'NEWLINE "\\n"',
      'NL "\\n"',
      'NAME "path"',
      'OP "="',
      'NAME "sys"',
      'OP "."',
      'NAME "argv"',
      'OP "["',
      'NUMBER "1"',
      'OP "]"',
      'NEWLINE "\\n"',
      'NAME "for"',
      'NAME "info"',
      'NAME "in"',
      'NAME "tokenize"',
      'OP "("',
      'NAME "open"',
      'OP "("',
      'NAME "path"',
      'OP ","',
      'STRING "rb"',
      'OP ")"',
      'OP "."',
      'NAME "readline"',
      'OP ")"',
      'OP ":"',
      'NEWLINE "\\n"',
      'INDENT "    "',
      'NAME "print"',
      'OP "("',
      'NAME "tok_name"',
      'OP "["',
      'NAME "info"',
      'OP "."',
      'NAME "type"',
      'OP "]"',
      'OP ","',
      'NAME "json"',
      'OP "."',
      'NAME "dumps"',
      'OP "("',
      'NAME "info"',
      'OP "."',
      'NAME "string"',
      'OP ")"',
      'OP ")"',
      'NEWLINE "\\n"',
      // 'NL "\\n"',
      'DEDENT ""',
      'ENDMARKER ""',
    ])
  })

  test("kurt python", () => {
    let tokens = python.outputTokens(fs.readFileSync('test/kurt.py', 'utf-8'))
    expect(tokens[100]).toBe('NAME "def"')
    expect(tokens.pop()).toBe('ENDMARKER ""')
    tokens.pop()
    expect(tokens.pop()).not.toBe('ERRORTOKEN ""')
    expect(tokens.length).toBe(11616)

    // let expected = fs.readFileSync('test/kurt-tokens.txt', 'utf-8').split('\n')
    // expect(tokens).toEqual(expected)
  })

})

describe('tosh tokenizer', () => {

  const tosh = require('./tosh')

  test('tosh', () => {
    let oldTokens = tosh.oldTokenizer(tosh.exampleFile)
    expect(tosh.tokenize(tosh.exampleFile)).toEqual(oldTokens)
  })

})
<|endoftext|>


<commit_before>'use strict';

require('should');
const checker = require('../index');
const os = require('os');
const execSync = require('child_process').execSync;

describe('check-tracked', () => {
  describe('success', () => {
    it('should resolve object with success true if tag can be used', () => {
      return checker('publish/success').should.be.fulfilledWith({
        success: true
      });
    });
  });
  describe('fail', () => {
    beforeEach(() => execSync('git tag -a publish/fail -m fail'));
    afterEach(() => execSync('git tag -d publish/fail'));
    it('should resolve object with success false if tag had been used', () => {
      return checker('publish/fail').should.be.fulfilledWith({
        success: false
      });
    });
  });
  describe('error', () => {
    it('should reject with an error when tag is not a string', () => {
      return checker({}).should.be.rejectedWith(TypeError, {
        message: 'Expected tag to be a string'
      });
    });
    it('should reject with an error when cwd is not a string', () => {
      return checker('publish/error', {}).should.be.rejectedWith(TypeError, {
        message: 'Expected cwd to be a string'
      });
    });
    it('should reject with an error when no git repository found', () => {
      let dir = os.tmpdir();
      return checker('publish/error', dir).should.be.rejectedWith(Error, {
        message: `No git repository was found in ${dir}`
      });
    });
  });
});
<commit_msg>:smile:<commit_after>'use strict';

require('should');
const checker = require('../index');
const os = require('os');
const execSync = require('child_process').execSync;

describe('check-tracked', () => {
  before(() => {
    execSync('git config user.email lingyucoder@gmail.com');
    execSync('git config user.name LingyuCoder');
  });
  describe('success', () => {
    it('should resolve object with success true if tag can be used', () => {
      return checker('publish/success').should.be.fulfilledWith({
        success: true
      });
    });
  });
  describe('fail', () => {
    beforeEach(() => execSync('git tag -a publish/fail -m fail'));
    afterEach(() => execSync('git tag -d publish/fail'));
    it('should resolve object with success false if tag had been used', () => {
      return checker('publish/fail').should.be.fulfilledWith({
        success: false
      });
    });
  });
  describe('error', () => {
    it('should reject with an error when tag is not a string', () => {
      return checker({}).should.be.rejectedWith(TypeError, {
        message: 'Expected tag to be a string'
      });
    });
    it('should reject with an error when cwd is not a string', () => {
      return checker('publish/error', {}).should.be.rejectedWith(TypeError, {
        message: 'Expected cwd to be a string'
      });
    });
    it('should reject with an error when no git repository found', () => {
      let dir = os.tmpdir();
      return checker('publish/error', dir).should.be.rejectedWith(Error, {
        message: `No git repository was found in ${dir}`
      });
    });
  });
});
<|endoftext|>


<commit_before>var assert = require('assert');
var util = require('util');

var create = require('..');

describe('HTTP Errors', function () {
  it('create(status)', function () {
    var err = create(404);
    assert.equal(err.name, 'NotFoundError')
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
  })

  it('create(status) for 300', function () {
    var err = create(300);
    assert.equal(err.message, 'Multiple Choices');
    assert.equal(err.status, 300);
    assert.equal(err.statusCode, 300);
  })

  it('create(status, msg)', function () {
    var err = create(404, 'LOL');
    assert.equal(err.name, 'NotFoundError')
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
  })

  it('create(status, props)', function () {
    var err = create(404, {
      id: 1
    })
    assert.equal(err.name, 'NotFoundError')
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
    assert.equal(1, err.id);
  })

  it('create(status, props) with status prop', function () {
    var err = create(404, {
      id: 1,
      status: 500
    })
    assert.equal(err.name, 'NotFoundError')
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
    assert.equal(1, err.id);
  })

  it('create(status, props) with statusCode prop', function () {
    var err = create(404, {
      id: 1,
      statusCode: 500
    })
    assert.equal(err.name, 'NotFoundError')
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
    assert.equal(1, err.id);
  })

  it('create(props)', function () {
    var err = create({
      id: 1
    });
    assert.equal(err.id, 1);
    assert.equal(err.name, 'InternalServerError')
    assert.equal(err.message, 'Internal Server Error');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
  })

  it('create(msg, status)', function () {
    var err = create('LOL', 404);
    assert.equal(err.name, 'NotFoundError')
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
  })

  it('create(msg)', function () {
    var err = create('LOL');
    assert.equal(err.name, 'InternalServerError')
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
  })

  it('create(msg, props)', function () {
    var err = create('LOL', {
      id: 1
    });
    assert.equal(err.name, 'InternalServerError')
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
    assert.equal(err.id, 1);
  })

  it ('create(err)', function () {
    var _err = new Error('LOL');
    _err.status = 404;
    var err = create(_err);
    assert.equal(err, _err);
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.expose, true);

    _err = new Error('LOL');
    err = create(_err);
    assert.equal(err, _err);
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
    assert.equal(err.expose, false);
  })

  it('create(err) with invalid err.status', function () {
    var _err = new Error('Connection refused');
    _err.status = -1;
    var err = create(_err);
    assert.equal(err, _err);
    assert.equal(err.message, 'Connection refused');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
    assert.equal(err.expose, false);
  })

  it('create(err, props)', function () {
    var _err = new Error('LOL');
    _err.status = 404;
    var err = create(_err, {
      id: 1
    });
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.id, 1);
    assert.equal(err.expose, true);
  })

  it('create(status, err, props)', function () {
    var _err = new Error('LOL');
    var err = create(404, _err, {
      id: 1
    });
    assert.equal(err, _err);
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
  })

  it('create(status, msg, props)', function () {
    var err = create(404, 'LOL', {
      id: 1
    });
    assert.equal(err.name, 'NotFoundError')
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
  })

  it('create(status, msg, { expose: false })', function () {
    var err = create(404, 'LOL', {
      expose: false
    })
    assert.equal(err.expose, false)
  })

  it('new create.NotFound()', function () {
    var err = new create.NotFound();
    assert.equal(err.name, 'NotFoundError')
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
    assert.equal(err.expose, true);
    assert(err.stack);
  })

  it('new create.InternalServerError()', function () {
    var err = new create.InternalServerError();
    assert.equal(err.name, 'InternalServerError')
    assert.equal(err.message, 'Internal Server Error');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
    assert.equal(err.expose, false);
    assert(err.stack);
  })

  it('new create["404"]()', function () {
    var err = new create['404']();
    assert.equal(err.name, 'NotFoundError')
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
    assert.equal(err.expose, true);
    assert(err.stack);
  })

  it('should preserve error [[Class]]', function () {
    var err = new create('LOL');
    assert.equal(Object.prototype.toString.call(err), '[object Error]')

    var err = new create[404]();
    assert.equal(Object.prototype.toString.call(err), '[object Error]')

    var err = new create[500]();
    assert.equal(Object.prototype.toString.call(err), '[object Error]')
  })

  it('should support err instanceof Error', function () {
    assert(create(404) instanceof Error);
    assert((new create['404']()) instanceof Error);
    assert((new create['500']()) instanceof Error);
  })

  it('should support util.isError()', function () {
    assert(util.isError(create(404)));
    assert(util.isError(new create['404']()));
    assert(util.isError(new create['500']()));
  })
})
<commit_msg>tests: add missing semicolons<commit_after>var assert = require('assert');
var util = require('util');

var create = require('..');

describe('HTTP Errors', function () {
  it('create(status)', function () {
    var err = create(404);
    assert.equal(err.name, 'NotFoundError');
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
  })

  it('create(status) for 300', function () {
    var err = create(300);
    assert.equal(err.message, 'Multiple Choices');
    assert.equal(err.status, 300);
    assert.equal(err.statusCode, 300);
  })

  it('create(status, msg)', function () {
    var err = create(404, 'LOL');
    assert.equal(err.name, 'NotFoundError');
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
  })

  it('create(status, props)', function () {
    var err = create(404, {
      id: 1
    });
    assert.equal(err.name, 'NotFoundError');
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
    assert.equal(1, err.id);
  })

  it('create(status, props) with status prop', function () {
    var err = create(404, {
      id: 1,
      status: 500
    });
    assert.equal(err.name, 'NotFoundError');
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
    assert.equal(1, err.id);
  })

  it('create(status, props) with statusCode prop', function () {
    var err = create(404, {
      id: 1,
      statusCode: 500
    });
    assert.equal(err.name, 'NotFoundError');
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
    assert.equal(1, err.id);
  })

  it('create(props)', function () {
    var err = create({
      id: 1
    });
    assert.equal(err.id, 1);
    assert.equal(err.name, 'InternalServerError');
    assert.equal(err.message, 'Internal Server Error');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
  })

  it('create(msg, status)', function () {
    var err = create('LOL', 404);
    assert.equal(err.name, 'NotFoundError');
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
  })

  it('create(msg)', function () {
    var err = create('LOL');
    assert.equal(err.name, 'InternalServerError');
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
  })

  it('create(msg, props)', function () {
    var err = create('LOL', {
      id: 1
    });
    assert.equal(err.name, 'InternalServerError');
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
    assert.equal(err.id, 1);
  })

  it ('create(err)', function () {
    var _err = new Error('LOL');
    _err.status = 404;
    var err = create(_err);
    assert.equal(err, _err);
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.expose, true);

    _err = new Error('LOL');
    err = create(_err);
    assert.equal(err, _err);
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
    assert.equal(err.expose, false);
  })

  it('create(err) with invalid err.status', function () {
    var _err = new Error('Connection refused');
    _err.status = -1;
    var err = create(_err);
    assert.equal(err, _err);
    assert.equal(err.message, 'Connection refused');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
    assert.equal(err.expose, false);
  })

  it('create(err, props)', function () {
    var _err = new Error('LOL');
    _err.status = 404;
    var err = create(_err, {
      id: 1
    });
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.id, 1);
    assert.equal(err.expose, true);
  })

  it('create(status, err, props)', function () {
    var _err = new Error('LOL');
    var err = create(404, _err, {
      id: 1
    });
    assert.equal(err, _err);
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
  })

  it('create(status, msg, props)', function () {
    var err = create(404, 'LOL', {
      id: 1
    });
    assert.equal(err.name, 'NotFoundError')
    assert.equal(err.message, 'LOL');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
  })

  it('create(status, msg, { expose: false })', function () {
    var err = create(404, 'LOL', {
      expose: false
    });
    assert.equal(err.expose, false);
  })

  it('new create.NotFound()', function () {
    var err = new create.NotFound();
    assert.equal(err.name, 'NotFoundError');
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
    assert.equal(err.expose, true);
    assert(err.stack);
  })

  it('new create.InternalServerError()', function () {
    var err = new create.InternalServerError();
    assert.equal(err.name, 'InternalServerError');
    assert.equal(err.message, 'Internal Server Error');
    assert.equal(err.status, 500);
    assert.equal(err.statusCode, 500);
    assert.equal(err.expose, false);
    assert(err.stack);
  })

  it('new create["404"]()', function () {
    var err = new create['404']();
    assert.equal(err.name, 'NotFoundError');
    assert.equal(err.message, 'Not Found');
    assert.equal(err.status, 404);
    assert.equal(err.statusCode, 404);
    assert.equal(err.expose, true);
    assert(err.stack);
  })

  it('should preserve error [[Class]]', function () {
    var err = new create('LOL');
    assert.equal(Object.prototype.toString.call(err), '[object Error]');

    var err = new create[404]();
    assert.equal(Object.prototype.toString.call(err), '[object Error]');

    var err = new create[500]();
    assert.equal(Object.prototype.toString.call(err), '[object Error]');
  })

  it('should support err instanceof Error', function () {
    assert(create(404) instanceof Error);
    assert((new create['404']()) instanceof Error);
    assert((new create['500']()) instanceof Error);
  })

  it('should support util.isError()', function () {
    assert(util.isError(create(404)));
    assert(util.isError(new create['404']()));
    assert(util.isError(new create['500']()));
  })
})
<|endoftext|>


<commit_before>/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @emails react-core
 */

'use strict';

var React = require('React');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactTestUtils = require('ReactTestUtils');

/**
 * Ensure that all callbacks are invoked, passing this unique argument.
 */
var ARG = {arg: true};
var ARG2 = {arg2: true};

class ChildComponent extends React.Component {
  render() {
    return (

    );
  }
}

class ParentComponent extends React.Component {
  render() {
    return (

          <ChildComponent ref="P_P1_C1" id="P_P1_C1" />
          <ChildComponent ref="P_P1_C2" id="P_P1_C2" />

    );
  }
}

function renderParentIntoDocument() {
  return ReactTestUtils.renderIntoDocument(<ParentComponent />);
}

describe('ReactTreeTraversal', () => {
  var ReactTreeTraversal;

  var aggregatedArgs;
  function argAggregator(inst, phase, arg) {
    aggregatedArgs.push({
      node: ReactDOMComponentTree.getNodeFromInstance(inst).id,
      phase: phase,
      arg: arg,
    });
  }

  function getInst(node) {
    return ReactDOMComponentTree.getInstanceFromNode(node);
  }

  beforeEach(() => {
    ReactTreeTraversal = require('ReactTreeTraversal');
    aggregatedArgs = [];
  });

  describe('traverseTwoPhase', () => {
    it('should not traverse when traversing outside DOM', () => {
      var expectedAggregation = [];
      ReactTreeTraversal.traverseTwoPhase(null, argAggregator, ARG);
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });

    it('should traverse two phase across component boundary', () => {
      var parent = renderParentIntoDocument();
      var target = getInst(parent.refs.P_P1_C1.refs.DIV_1);
      var expectedAggregation = [
        {node: 'P', phase: 'captured', arg: ARG},
        {node: 'P_P1', phase: 'captured', arg: ARG},
        {node: 'P_P1_C1__DIV', phase: 'captured', arg: ARG},
        {node: 'P_P1_C1__DIV_1', phase: 'captured', arg: ARG},

        {node: 'P_P1_C1__DIV_1', phase: 'bubbled', arg: ARG},
        {node: 'P_P1_C1__DIV', phase: 'bubbled', arg: ARG},
        {node: 'P_P1', phase: 'bubbled', arg: ARG},
        {node: 'P', phase: 'bubbled', arg: ARG},
      ];
      ReactTreeTraversal.traverseTwoPhase(target, argAggregator, ARG);
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });

    it('should traverse two phase at shallowest node', () => {
      var parent = renderParentIntoDocument();
      var target = getInst(parent.refs.P);
      var expectedAggregation = [
        {node: 'P', phase: 'captured', arg: ARG},
        {node: 'P', phase: 'bubbled', arg: ARG},
      ];
      ReactTreeTraversal.traverseTwoPhase(target, argAggregator, ARG);
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });
  });

  describe('traverseEnterLeave', () => {
    it('should not traverse when enter/leaving outside DOM', () => {
      var target = null;
      var expectedAggregation = [];
      ReactTreeTraversal.traverseEnterLeave(
        target, target, argAggregator, ARG, ARG2
      );
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });

    it('should not traverse if enter/leave the same node', () => {
      var parent = renderParentIntoDocument();
      var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1);
      var enter = getInst(parent.refs.P_P1_C1.refs.DIV_1);
      var expectedAggregation = [];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, argAggregator, ARG, ARG2
      );
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });

    it('should traverse enter/leave to sibling - avoids parent', () => {
      var parent = renderParentIntoDocument();
      var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1);
      var enter = getInst(parent.refs.P_P1_C1.refs.DIV_2);
      var expectedAggregation = [
        {node: 'P_P1_C1__DIV_1', phase: 'bubbled', arg: ARG},
        // enter/leave shouldn't fire anything on the parent
        {node: 'P_P1_C1__DIV_2', phase: 'captured', arg: ARG2},
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, argAggregator, ARG, ARG2
      );
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });

    it('should traverse enter/leave to parent - avoids parent', () => {
      var parent = renderParentIntoDocument();
      var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1);
      var enter = getInst(parent.refs.P_P1_C1.refs.DIV);
      var expectedAggregation = [
        {node: 'P_P1_C1__DIV_1', phase: 'bubbled', arg: ARG},
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, argAggregator, ARG, ARG2
      );
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });

    it('should enter from the window', () => {
      var parent = renderParentIntoDocument();
      var leave = null; // From the window or outside of the React sandbox.
      var enter = getInst(parent.refs.P_P1_C1.refs.DIV);
      var expectedAggregation = [
        {node: 'P', phase: 'captured', arg: ARG2},
        {node: 'P_P1', phase: 'captured', arg: ARG2},
        {node: 'P_P1_C1__DIV', phase: 'captured', arg: ARG2},
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, argAggregator, ARG, ARG2
      );
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });

    it('should enter from the window to the shallowest', () => {
      var parent = renderParentIntoDocument();
      var leave = null; // From the window or outside of the React sandbox.
      var enter = getInst(parent.refs.P);
      var expectedAggregation = [
        {node: 'P', phase: 'captured', arg: ARG2},
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, argAggregator, ARG, ARG2
      );
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });

    it('should leave to the window', () => {
      var parent = renderParentIntoDocument();
      var enter = null; // From the window or outside of the React sandbox.
      var leave = getInst(parent.refs.P_P1_C1.refs.DIV);
      var expectedAggregation = [
        {node: 'P_P1_C1__DIV', phase: 'bubbled', arg: ARG},
        {node: 'P_P1', phase: 'bubbled', arg: ARG},
        {node: 'P', phase: 'bubbled', arg: ARG},
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, argAggregator, ARG, ARG2
      );
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });

    it('should leave to the window from the shallowest', () => {
      var parent = renderParentIntoDocument();
      var enter = null; // From the window or outside of the React sandbox.
      var leave = getInst(parent.refs.P_P1_C1.refs.DIV);
      var expectedAggregation = [
        {node: 'P_P1_C1__DIV', phase: 'bubbled', arg: ARG},
        {node: 'P_P1', phase: 'bubbled', arg: ARG},
        {node: 'P', phase: 'bubbled', arg: ARG},
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, argAggregator, ARG, ARG2
      );
      expect(aggregatedArgs).toEqual(expectedAggregation);
    });
  });

  describe('getFirstCommonAncestor', () => {
    it('should determine the first common ancestor correctly', () => {
      var parent = renderParentIntoDocument();
      var ancestors = [
        // Common ancestor with self is self.
        {one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_P1_C1.refs.DIV_1,
          com: parent.refs.P_P1_C1.refs.DIV_1,
        },
        // Common ancestor with self is self - even if topmost DOM.
        {one: parent.refs.P, two: parent.refs.P, com: parent.refs.P},
        // Siblings
        {
          one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_P1_C1.refs.DIV_2,
          com: parent.refs.P_P1_C1.refs.DIV,
        },
        // Common ancestor with parent is the parent.
        {
          one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_P1_C1.refs.DIV,
          com: parent.refs.P_P1_C1.refs.DIV,
        },
        // Common ancestor with grandparent is the grandparent.
        {
          one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_P1,
          com: parent.refs.P_P1,
        },
        // Grandparent across subcomponent boundaries.
        {
          one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_P1_C2.refs.DIV_1,
          com: parent.refs.P_P1,
        },
        // Something deep with something one-off.
        {
          one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_OneOff,
          com: parent.refs.P,
        },
      ];
      var i;
      for (i = 0; i < ancestors.length; i++) {
        var plan = ancestors[i];
        var firstCommon = ReactTreeTraversal.getLowestCommonAncestor(
          getInst(plan.one),
          getInst(plan.two)
        );
        expect(firstCommon).toBe(getInst(plan.com));
      }
    });
  });

});
<commit_msg>Improve tests (#8866)<commit_after>/**
 * Copyright 2013-present, Facebook, Inc.
 * All rights reserved.
 *
 * This source code is licensed under the BSD-style license found in the
 * LICENSE file in the root directory of this source tree. An additional grant
 * of patent rights can be found in the PATENTS file in the same directory.
 *
 * @emails react-core
 */

'use strict';

var React = require('React');
var ReactDOMComponentTree = require('ReactDOMComponentTree');
var ReactTestUtils = require('ReactTestUtils');

/**
 * Ensure that all callbacks are invoked, passing this unique argument.
 */
var ARG = {arg: true};
var ARG2 = {arg2: true};

class ChildComponent extends React.Component {
  render() {
    return (

    );
  }
}

class ParentComponent extends React.Component {
  render() {
    return (

          <ChildComponent ref="P_P1_C1" id="P_P1_C1" />
          <ChildComponent ref="P_P1_C2" id="P_P1_C2" />

    );
  }
}

function renderParentIntoDocument() {
  return ReactTestUtils.renderIntoDocument(<ParentComponent />);
}

describe('ReactTreeTraversal', () => {
  var ReactTreeTraversal;
  var mockFn = jest.fn();

  function callback(inst, phase, arg) {
    mockFn(ReactDOMComponentTree.getNodeFromInstance(inst).id, phase, arg);
  }

  function getInst(node) {
    return ReactDOMComponentTree.getInstanceFromNode(node);
  }

  beforeEach(() => {
    ReactTreeTraversal = require('ReactTreeTraversal');
    mockFn.mockReset();
  });

  describe('traverseTwoPhase', () => {
    it('should not traverse when traversing outside DOM', () => {
      ReactTreeTraversal.traverseTwoPhase(null, callback, ARG);
      expect(mockFn).not.toHaveBeenCalled();
    });

    it('should traverse two phase across component boundary', () => {
      var parent = renderParentIntoDocument();
      var target = getInst(parent.refs.P_P1_C1.refs.DIV_1);
      var expectedCalls = [
        ['P', 'captured', ARG],
        ['P_P1', 'captured', ARG],
        ['P_P1_C1__DIV', 'captured', ARG],
        ['P_P1_C1__DIV_1', 'captured', ARG],

        ['P_P1_C1__DIV_1', 'bubbled', ARG],
        ['P_P1_C1__DIV', 'bubbled', ARG],
        ['P_P1', 'bubbled', ARG],
        ['P', 'bubbled', ARG],
      ];
      ReactTreeTraversal.traverseTwoPhase(target, callback, ARG);
      expect(mockFn.mock.calls).toEqual(expectedCalls);
    });

    it('should traverse two phase at shallowest node', () => {
      var parent = renderParentIntoDocument();
      var target = getInst(parent.refs.P);
      var expectedCalls = [
        ['P', 'captured', ARG],
        ['P', 'bubbled', ARG],
      ];
      ReactTreeTraversal.traverseTwoPhase(target, callback, ARG);
      expect(mockFn.mock.calls).toEqual(expectedCalls);
    });
  });

  describe('traverseEnterLeave', () => {
    it('should not traverse when enter/leaving outside DOM', () => {
      ReactTreeTraversal.traverseEnterLeave(
        null, null, callback, ARG, ARG2
      );
      expect(mockFn).not.toHaveBeenCalled();
    });

    it('should not traverse if enter/leave the same node', () => {
      var parent = renderParentIntoDocument();
      var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1);
      var enter = getInst(parent.refs.P_P1_C1.refs.DIV_1);
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, callback, ARG, ARG2
      );
      expect(mockFn).not.toHaveBeenCalled();
    });

    it('should traverse enter/leave to sibling - avoids parent', () => {
      var parent = renderParentIntoDocument();
      var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1);
      var enter = getInst(parent.refs.P_P1_C1.refs.DIV_2);
      var expectedCalls = [
        ['P_P1_C1__DIV_1', 'bubbled', ARG],
        // enter/leave shouldn't fire anything on the parent
        ['P_P1_C1__DIV_2', 'captured', ARG2],
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, callback, ARG, ARG2
      );
      expect(mockFn.mock.calls).toEqual(expectedCalls);
    });

    it('should traverse enter/leave to parent - avoids parent', () => {
      var parent = renderParentIntoDocument();
      var leave = getInst(parent.refs.P_P1_C1.refs.DIV_1);
      var enter = getInst(parent.refs.P_P1_C1.refs.DIV);
      var expectedCalls = [
        ['P_P1_C1__DIV_1', 'bubbled', ARG],
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, callback, ARG, ARG2
      );
      expect(mockFn.mock.calls).toEqual(expectedCalls);
    });

    it('should enter from the window', () => {
      var parent = renderParentIntoDocument();
      var leave = null; // From the window or outside of the React sandbox.
      var enter = getInst(parent.refs.P_P1_C1.refs.DIV);
      var expectedCalls = [
        ['P', 'captured', ARG2],
        ['P_P1', 'captured', ARG2],
        ['P_P1_C1__DIV', 'captured', ARG2],
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, callback, ARG, ARG2
      );
      expect(mockFn.mock.calls).toEqual(expectedCalls);
    });

    it('should enter from the window to the shallowest', () => {
      var parent = renderParentIntoDocument();
      var leave = null; // From the window or outside of the React sandbox.
      var enter = getInst(parent.refs.P);
      var expectedCalls = [
        ['P', 'captured', ARG2],
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, callback, ARG, ARG2
      );
      expect(mockFn.mock.calls).toEqual(expectedCalls);
    });

    it('should leave to the window', () => {
      var parent = renderParentIntoDocument();
      var enter = null; // From the window or outside of the React sandbox.
      var leave = getInst(parent.refs.P_P1_C1.refs.DIV);
      var expectedCalls = [
        ['P_P1_C1__DIV', 'bubbled', ARG],
        ['P_P1', 'bubbled', ARG],
        ['P', 'bubbled', ARG],
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, callback, ARG, ARG2
      );
      expect(mockFn.mock.calls).toEqual(expectedCalls);
    });

    it('should leave to the window from the shallowest', () => {
      var parent = renderParentIntoDocument();
      var enter = null; // From the window or outside of the React sandbox.
      var leave = getInst(parent.refs.P_P1_C1.refs.DIV);
      var expectedCalls = [
        ['P_P1_C1__DIV', 'bubbled', ARG],
        ['P_P1', 'bubbled', ARG],
        ['P', 'bubbled', ARG],
      ];
      ReactTreeTraversal.traverseEnterLeave(
        leave, enter, callback, ARG, ARG2
      );
      expect(mockFn.mock.calls).toEqual(expectedCalls);
    });
  });

  describe('getFirstCommonAncestor', () => {
    it('should determine the first common ancestor correctly', () => {
      var parent = renderParentIntoDocument();
      var ancestors = [
        // Common ancestor with self is self.
        {one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_P1_C1.refs.DIV_1,
          com: parent.refs.P_P1_C1.refs.DIV_1,
        },
        // Common ancestor with self is self - even if topmost DOM.
        {one: parent.refs.P, two: parent.refs.P, com: parent.refs.P},
        // Siblings
        {
          one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_P1_C1.refs.DIV_2,
          com: parent.refs.P_P1_C1.refs.DIV,
        },
        // Common ancestor with parent is the parent.
        {
          one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_P1_C1.refs.DIV,
          com: parent.refs.P_P1_C1.refs.DIV,
        },
        // Common ancestor with grandparent is the grandparent.
        {
          one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_P1,
          com: parent.refs.P_P1,
        },
        // Grandparent across subcomponent boundaries.
        {
          one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_P1_C2.refs.DIV_1,
          com: parent.refs.P_P1,
        },
        // Something deep with something one-off.
        {
          one: parent.refs.P_P1_C1.refs.DIV_1,
          two: parent.refs.P_OneOff,
          com: parent.refs.P,
        },
      ];
      var i;
      for (i = 0; i < ancestors.length; i++) {
        var plan = ancestors[i];
        var firstCommon = ReactTreeTraversal.getLowestCommonAncestor(
          getInst(plan.one),
          getInst(plan.two)
        );
        expect(firstCommon).toBe(getInst(plan.com));
      }
    });
  });

});
<|endoftext|>