suite('Shape', function() {
  // ======================================================
  test('test intersects()', function() {
    var stage = addStage();
    var layer = new Konva.Layer();
    var rect = new Konva.Rect({
      x: 200,
      y: 100,
      width: 100,
      height: 50,
      fill: 'green',
      stroke: 'black',
      strokeWidth: 4
    });

    layer.add(rect);
    stage.add(layer);

    assert.equal(
      rect.intersects({
        x: 201,
        y: 101
      }),
      true,
      '(201,101) should intersect the shape'
    );

    assert.equal(
      rect.intersects({
        x: 197,
        y: 97
      }),
      false,
      '(197, 97) should not intersect the shape'
    );

    assert.equal(
      rect.intersects({
        x: 250,
        y: 125
      }),
      true,
      '(250, 125) should intersect the shape'
    );

    assert.equal(
      rect.intersects({
        x: 300,
        y: 150
      }),
      true,
      '(300, 150) should intersect the shape'
    );

    assert.equal(
      rect.intersects({
        x: 303,
        y: 153
      }),
      false,
      '(303, 153) should not intersect the shape'
    );
  });

  // ======================================================
  test('test hasShadow() method', function() {
    var stage = addStage();
    var layer = new Konva.Layer();
    var shape = new Konva.Shape({
      sceneFunc: function(context) {
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(100, 0);
        context.lineTo(100, 100);
        context.closePath();
        context.fillStrokeShape(this);
      },
      x: 10,
      y: 10,
      fill: 'green',
      stroke: 'blue',
      strokeWidth: 5,
      shadowColor: 'black',
      shadowOffset: 10,
      shadowOpacity: 0
    });

    layer.add(shape);
    stage.add(layer);

    assert.equal(
      shape.hasShadow(),
      false,
      'shape should not have a shadow because opacity is 0'
    );

    shape.setShadowOpacity(0.5);

    assert.equal(
      shape.hasShadow(),
      true,
      'shape should have a shadow because opacity is nonzero'
    );

    shape.setShadowEnabled(false);

    assert.equal(
      shape.hasShadow(),
      false,
      'shape should not have a shadow because it is not enabled'
    );
  });

  // ======================================================
  test('custom shape with fill, stroke, and strokeWidth', function() {
    var stage = addStage();
    var layer = new Konva.Layer();
    var shape = new Konva.Shape({
      sceneFunc: function(context) {
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(100, 0);
        context.lineTo(100, 100);
        context.closePath();
        context.fillStrokeShape(this);
      },
      x: 200,
      y: 100,
      fill: 'green',
      stroke: 'blue',
      strokeWidth: 5
    });

    layer.add(shape);
    stage.add(layer);
  });

  // ======================================================
  test('add star with translated, scaled, rotated fill', function(done) {
    var imageObj = new Image();
    imageObj.onload = function() {
      var stage = addStage();
      var layer = new Konva.Layer();

      var star = new Konva.Star({
        x: 200,
        y: 100,
        numPoints: 5,
        innerRadius: 40,
        outerRadius: 70,

        fillPatternImage: imageObj,
        fillPatternX: -20,
        fillPatternY: -30,
        fillPatternScale: { x: 0.5, y: 0.5 },
        fillPatternOffset: { x: 219, y: 150 },
        fillPatternRotation: 90,
        fillPatternRepeat: 'no-repeat',

        stroke: 'blue',
        strokeWidth: 5,
        draggable: true
      });

      layer.add(star);
      stage.add(layer);

      /*
             var anim = new Konva.Animation(function() {
             star.attrs.fill.rotation += 0.02;
             }, layer);
             anim.start();
             */

      assert.equal(star.getFillPatternX(), -20, 'star fill x should be -20');
      assert.equal(star.getFillPatternY(), -30, 'star fill y should be -30');
      assert.equal(
        star.getFillPatternScale().x,
        0.5,
        'star fill scale x should be 0.5'
      );
      assert.equal(
        star.getFillPatternScale().y,
        0.5,
        'star fill scale y should be 0.5'
      );
      assert.equal(
        star.getFillPatternOffset().x,
        219,
        'star fill offset x should be 219'
      );
      assert.equal(
        star.getFillPatternOffset().y,
        150,
        'star fill offset y should be 150'
      );
      assert.equal(
        star.getFillPatternRotation(),
        90,
        'star fill rotation should be 90'
      );

      star.setFillPatternRotation(180);

      assert.equal(
        star.getFillPatternRotation(),
        180,
        'star fill rotation should be 180'
      );

      star.setFillPatternScale({ x: 1, y: 1 });

      assert.equal(
        star.getFillPatternScale().x,
        1,
        'star fill scale x should be 1'
      );
      assert.equal(
        star.getFillPatternScale().y,
        1,
        'star fill scale y should be 1'
      );

      star.setFillPatternOffset({ x: 100, y: 120 });

      assert.equal(
        star.getFillPatternOffset().x,
        100,
        'star fill offset x should be 100'
      );
      assert.equal(
        star.getFillPatternOffset().y,
        120,
        'star fill offset y should be 120'
      );

      done();
    };
    imageObj.src = 'assets/darth-vader.jpg';
  });

  // ======================================================
  test('test size setters and getters', function() {
    var stage = addStage();
    var layer = new Konva.Layer();

    var circle = new Konva.Circle({
      x: stage.getWidth() / 2,
      y: stage.getHeight() / 2,
      radius: 50,
      fill: 'red'
    });

    var ellipse = new Konva.Circle({
      x: stage.getWidth() / 2,
      y: stage.getHeight() / 2,
      radius: 50,
      fill: 'yellow'
    });

    layer.add(ellipse);
    layer.add(circle);
    stage.add(layer);

    // circle tests
    assert.equal(circle.getWidth(), 100, 'circle width should be 100');
    assert.equal(circle.getHeight(), 100, 'circle height should be 100');
    assert.equal(circle.getSize().width, 100, 'circle width should be 100');
    assert.equal(circle.getSize().height, 100, 'circle height should be 100');
    assert.equal(circle.getRadius(), 50, 'circle radius should be 50');

    circle.setWidth(200);

    assert.equal(circle.getWidth(), 200, 'circle width should be 200');
    assert.equal(circle.getHeight(), 200, 'circle height should be 200');
    assert.equal(circle.getSize().width, 200, 'circle width should be 200');
    assert.equal(circle.getSize().height, 200, 'circle height should be 200');
    assert.equal(circle.getRadius(), 100, 'circle radius should be 100');
  });

  // ======================================================
  test('set image fill to color then image then linear gradient then back to image', function(done) {
    var imageObj = new Image();
    imageObj.onload = function() {
      var stage = addStage();
      var layer = new Konva.Layer();
      var circle = new Konva.Circle({
        x: 200,
        y: 60,
        radius: 50,
        fill: 'blue'
      });

      layer.add(circle);
      stage.add(layer);

      assert.equal(circle.getFill(), 'blue', 'circle fill should be blue');

      circle.setFill(null);
      circle.setFillPatternImage(imageObj);
      circle.setFillPatternRepeat('no-repeat');
      circle.setFillPatternOffset({ x: -200, y: -70 });

      assert.notEqual(
        circle.getFillPatternImage(),
        undefined,
        'circle fill image should be defined'
      );
      assert.equal(
        circle.getFillPatternRepeat(),
        'no-repeat',
        'circle fill repeat should be no-repeat'
      );
      assert.equal(
        circle.getFillPatternOffset().x,
        -200,
        'circle fill offset x should be -200'
      );
      assert.equal(
        circle.getFillPatternOffset().y,
        -70,
        'circle fill offset y should be -70'
      );

      circle.setFillPatternImage(null);
      circle.setFillLinearGradientStartPoint({ x: -35, y: -35 });
      circle.setFillLinearGradientEndPoint({ x: 35, y: 35 });
      circle.setFillLinearGradientColorStops([0, 'red', 1, 'blue']);

      circle.setFillLinearGradientStartPoint(null);
      circle.setFillPatternImage(imageObj);
      circle.setFillPatternRepeat('repeat');
      circle.setFillPatternOffset({ x: 0, y: 0 });

      layer.draw();

      done();
    };
    imageObj.src = 'assets/darth-vader.jpg';
  });

  test('stroke gradient', function() {
    var stage = addStage();
    var layer = new Konva.Layer({
      scaleY: 1.5
    });

    var shape = new Konva.Rect({
      x: 10,
      y: 10,
      width: 100,
      height: 100,
      fillLinearGradientColorStops: [0, 'yellow', 0.5, 'red', 1, 'white'],
      fillLinearGradientStartPoint: {
        x: 0,
        y: 0
      },
      scaleX: 3,
      fillLinearGradientEndPoint: {
        x: 100,
        y: 100
      },
      strokeLinearGradientColorStops: [0, 'red', 0.5, 'blue', 1, 'green'],
      strokeLinearGradientStartPoint: {
        x: 0,
        y: 0
      },
      strokeLinearGradientEndPoint: {
        x: 100,
        y: 100
      }
    });
    layer.add(shape);
    stage.add(layer);

    var trace = layer.getContext().getTrace();

    assert.equal(
      trace,
      'clearRect(0,0,578,200);save();transform(3,0,0,1.5,10,15);beginPath();rect(0,0,100,100);closePath();createLinearGradient(0,0,100,100);fillStyle=[object CanvasGradient];fill();lineWidth=2;createLinearGradient(0,0,100,100);strokeStyle=[object CanvasGradient];stroke();restore();'
    );
  });

  // ======================================================
  test('test enablers and disablers', function() {
    var stage = addStage();
    var layer = new Konva.Layer();
    var circle = new Konva.Circle({
      x: stage.getWidth() / 2,
      y: stage.getHeight() / 2,
      radius: 70,
      fill: 'green',
      stroke: 'black',
      strokeWidth: 4,
      shadowColor: 'black',
      shadowBlur: 10,
      shadowOffset: { x: 10, y: 10 },
      dash: [10, 10],
      scaleX: 3
    });
    layer.add(circle);
    stage.add(layer);

    assert.equal(circle.getStrokeScaleEnabled(), true);
    assert.equal(circle.getFillEnabled(), true, 'fillEnabled should be true');
    assert.equal(
      circle.getStrokeEnabled(),
      true,
      'strokeEnabled should be true'
    );
    assert.equal(
      circle.getShadowEnabled(),
      true,
      'shadowEnabled should be true'
    );
    assert.equal(circle.dashEnabled(), true, 'dashEnabled should be true');

    circle.strokeScaleEnabled(false);
    assert.equal(circle.getStrokeScaleEnabled(), false);

    layer.draw();
    //        var trace = layer.getContext().getTrace();

    circle.fillEnabled(false);
    assert.equal(circle.getFillEnabled(), false, 'fillEnabled should be false');

    circle.strokeEnabled(false);
    assert.equal(
      circle.getStrokeEnabled(),
      false,
      'strokeEnabled should be false'
    );

    circle.shadowEnabled(false);
    assert.equal(
      circle.getShadowEnabled(),
      false,
      'shadowEnabled should be false'
    );

    circle.dashEnabled(false);
    assert.equal(circle.dashEnabled(), false, 'dashEnabled should be false');

    // re-enable

    circle.dashEnabled(true);
    assert.equal(circle.getDashEnabled(), true, 'dashEnabled should be true');

    circle.shadowEnabled(true);
    assert.equal(
      circle.getShadowEnabled(),
      true,
      'shadowEnabled should be true'
    );

    circle.strokeEnabled(true);
    assert.equal(
      circle.getStrokeEnabled(),
      true,
      'strokeEnabled should be true'
    );

    circle.fillEnabled(true);
    assert.equal(circle.getFillEnabled(), true, 'fillEnabled should be true');
  });

  // ======================================================
  test('fill with shadow and opacity', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 50,
      width: 100,
      height: 50,
      fill: 'green',
      opacity: 0.5,
      shadowColor: 'black',
      shadowBlur: 10,
      shadowOffset: { x: 10, y: 10 },
      shadowOpacity: 0.5
    });

    layer.add(rect);
    stage.add(layer);

    assert.equal(rect.getX(), 100);
    assert.equal(rect.getY(), 50);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');
    context.globalAlpha = 0.5;
    // rect
    context.beginPath();
    context.rect(100, 50, 100, 50);
    context.closePath();

    context.fillStyle = 'green';
    context.shadowColor = 'rgba(0,0,0,0.5)';
    context.shadowBlur = 10 * canvas.ratio;
    context.shadowOffsetX = 10 * canvas.ratio;
    context.shadowOffsetY = 10 * canvas.ratio;
    context.fill();

    compareLayerAndCanvas(layer, canvas, 30);

    var trace = layer.getContext().getTrace();

    assert.equal(
      trace,
      'clearRect(0,0,578,200);save();transform(1,0,0,1,100,50);save();globalAlpha=0.5;shadowColor=rgba(0,0,0,0.5);shadowBlur=10;shadowOffsetX=10;shadowOffsetY=10;beginPath();rect(0,0,100,50);closePath();fillStyle=green;fill();restore();restore();'
    );
  });

  // ======================================================
  test('stroke with shadow and opacity', function() {
    Konva.pixelRatio = 1;
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 50,
      width: 100,
      height: 50,
      stroke: 'red',
      strokeWidth: 20,
      opacity: 0.5,
      shadowColor: 'black',
      shadowBlur: 10,
      shadowOffset: { x: 10, y: 10 },
      shadowOpacity: 0.5
    });

    layer.add(rect);
    stage.add(layer);

    assert.equal(rect.getX(), 100);
    assert.equal(rect.getY(), 50);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');
    context.globalAlpha = 0.5;
    // rect
    context.beginPath();
    context.rect(100, 50, 100, 50);
    context.closePath();

    context.strokeStyle = 'red';
    context.lineWidth = 20;

    context.shadowColor = 'rgba(0,0,0,0.5)';
    context.shadowBlur = 10 * canvas.ratio;
    context.shadowOffsetX = 10 * canvas.ratio;
    context.shadowOffsetY = 10 * canvas.ratio;
    context.stroke();

    compareLayerAndCanvas(layer, canvas, 10);

    var trace = layer.getContext().getTrace();
    //console.log(trace);
    assert.equal(
      trace,
      'clearRect(0,0,578,200);save();transform(1,0,0,1,100,50);save();globalAlpha=0.5;shadowColor=rgba(0,0,0,0.5);shadowBlur=10;shadowOffsetX=10;shadowOffsetY=10;beginPath();rect(0,0,100,50);closePath();lineWidth=20;strokeStyle=red;stroke();restore();restore();'
    );
  });

  // ======================================================
  test('fill and stroke with opacity', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 50,
      width: 100,
      height: 50,
      fill: 'green',
      stroke: 'black',
      strokeWidth: 10,
      opacity: 0.5
    });

    layer.add(rect);

    stage.add(layer);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');
    context.globalAlpha = 0.5;
    // stroke
    context.beginPath();
    context.moveTo(100, 50);
    context.lineTo(200, 50);
    context.lineTo(200, 100);
    context.lineTo(100, 100);
    context.closePath();
    context.lineWidth = 10;
    context.strokeStyle = 'black';
    context.stroke();

    // rect
    context.fillStyle = 'green';
    context.fillRect(105, 55, 90, 40);

    compareLayerAndCanvas(layer, canvas, 10);
  });

  // ======================================================
  test('fill and stroke with shadow', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 50,
      width: 100,
      height: 50,
      fill: 'green',
      stroke: 'black',
      strokeWidth: 10,
      shadowColor: 'grey',
      shadowBlur: 10,
      shadowOffset: {
        x: 20,
        y: 20
      }
    });

    layer.add(rect);
    stage.add(layer);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');
    context.beginPath();
    context.rect(100, 50, 100, 50);
    context.closePath();
    context.fillStyle = 'green';
    context.shadowColor = 'grey';
    context.shadowBlur = 10 * canvas.ratio;
    context.shadowOffsetX = 20 * canvas.ratio;
    context.shadowOffsetY = 20 * canvas.ratio;
    context.lineWidth = 10;
    context.stroke();
    context.fill();

    // clear the shadow
    context.shadowColor = 0;
    context.shadowOffsetX = 0;
    context.shadowOffsetY = 0;
    context.shadowBlur = 0;

    // restroke without the shaodw
    context.stroke();

    compareLayerAndCanvas(layer, canvas, 10);

    var trace = layer.getContext().getTrace();
    //console.log(trace);
    assert.equal(
      trace,
      'clearRect(0,0,578,200);save();transform(1,0,0,1,100,50);save();shadowColor=rgba(128,128,128,1);shadowBlur=10;shadowOffsetX=20;shadowOffsetY=20;beginPath();rect(0,0,100,50);closePath();fillStyle=green;fill();lineWidth=10;strokeStyle=black;stroke();restore();beginPath();rect(0,0,100,50);closePath();fillStyle=green;fill();lineWidth=10;strokeStyle=black;stroke();restore();'
    );
  });

  // ======================================================
  // hard to emulate the same drawing
  test.skip('fill and stroke with shadow and opacity', function() {
    var stage = addStage();
    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 50,
      width: 100,
      height: 50,
      fill: 'green',
      stroke: 'black',
      strokeWidth: 10,
      shadowColor: 'grey',
      opacity: 0.5,
      shadowBlur: 5,
      shadowOffset: {
        x: 20,
        y: 20
      }
    });

    layer.add(rect);
    stage.add(layer);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');
    context.globalAlpha = 0.3;

    // draw shadow
    context.save();
    context.beginPath();
    context.rect(95, 45, 110, 60);
    context.closePath();
    context.shadowColor = 'grey';
    context.shadowBlur = 5 * canvas.ratio;
    context.shadowOffsetX = 20 * canvas.ratio;
    context.shadowOffsetY = 20 * canvas.ratio;
    context.fillStyle = 'black';
    context.fill();
    context.restore();

    // draw "stroke"
    context.save();
    context.beginPath();
    context.moveTo(100, 50);
    context.lineTo(200, 50);
    context.lineTo(200, 100);
    context.lineTo(100, 100);
    context.closePath();
    context.lineWidth = 10;
    context.strokeStyle = 'black';
    context.stroke();
    context.restore();

    context.save();
    context.beginPath();
    context.fillStyle = 'green';
    context.rect(105, 55, 90, 40);
    context.closePath();
    context.fill();
    context.restore();

    // don't test in PhantomJS as it use old chrome engine
    // it it has opacity + shadow bug
    if (!window.mochaPhantomJS) {
      compareLayerAndCanvas(layer, canvas, 260);
    }

    var trace = layer.getContext().getTrace();
    //console.log(trace);
    assert.equal(
      trace,
      'clearRect(0,0,578,200);save();save();shadowColor=rgba(128,128,128,1);shadowBlur=1;shadowOffsetX=20;shadowOffsetY=20;globalAlpha=0.5;drawImage([object HTMLCanvasElement],0,0,578,200);restore();restore();'
    );
  });

  // ======================================================
  test('text with fill and stroke with shadow', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var text = new Konva.Text({
      x: 50,
      y: 50,
      text: 'Test TEXT',
      fontSize: 50,
      fill: 'green',
      stroke: 'black',
      strokeWidth: 2,
      shadowColor: 'grey',
      shadowBlur: 2,
      shadowOffset: {
        x: 20,
        y: 20
      }
    });

    layer.add(text);
    stage.add(layer);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');

    context.save();
    context.shadowColor = 'grey';
    context.shadowBlur = 2 * canvas.ratio;
    context.shadowOffsetX = 20 * canvas.ratio;
    context.shadowOffsetY = 20 * canvas.ratio;
    context.font = 'normal 50px Arial';
    context.textBaseline = 'middle';

    context.fillStyle = 'green';
    context.fillText('Test TEXT', 50, 75);

    context.lineWidth = 2;
    context.strokeStyle = 'black';
    context.strokeText('Test TEXT', 50, 75);

    context.stroke();
    context.fill();
    context.restore();

    // draw text again to remove shadow under stroke
    context.font = 'normal 50px Arial';
    context.textBaseline = 'middle';
    context.fillText('Test TEXT', 50, 75);
    context.fillStyle = 'green';
    context.fillText('Test TEXT', 50, 75);

    context.lineWidth = 2;
    context.strokeStyle = 'black';
    context.strokeText('Test TEXT', 50, 75);

    if (!window.isPhantomJS) {
      compareLayerAndCanvas(layer, canvas, 254);
    }
  });

  // ======================================================
  test('shape intersect with shadow', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      fill: '#ff0000',
      x: 50,
      y: 50,
      width: 200,
      height: 200,
      draggable: true,
      shadowColor: '#000' // if all shadow properties removed, works fine
    });
    layer.add(rect);
    stage.add(layer);

    //error here
    assert.equal(rect.intersects({ x: 52, y: 52 }), true);
    assert.equal(rect.intersects({ x: 45, y: 45 }), false);
  });

  // ======================================================
  test('overloaded getters and setters', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 50,
      width: 100,
      height: 50,
      fill: 'green',
      stroke: 'red',
      strokeWidth: 20,
      draggable: true
    });

    layer.add(rect);
    stage.add(layer);

    rect.stroke('blue');
    assert.equal(rect.stroke(), 'blue');

    rect.lineJoin('bevel');
    assert.equal(rect.lineJoin(), 'bevel');

    rect.lineCap('square');
    assert.equal(rect.lineCap(), 'square');

    rect.strokeWidth(8);
    assert.equal(rect.strokeWidth(), 8);

    rect.sceneFunc('function');
    assert.equal(rect.sceneFunc(), 'function');

    rect.hitFunc('function');
    assert.equal(rect.hitFunc(), 'function');

    rect.dash([1]);
    assert.equal(rect.dash()[0], 1);

    // NOTE: skipping the rest because it would take hours to test all possible methods.
    // This should hopefully be enough to test Factor overloaded methods
  });

  // ======================================================
  test('create image hit region', function(done) {
    var imageObj = new Image();

    var stage = addStage();
    var layer = new Konva.Layer();

    imageObj.onload = function() {
      var lion = new Konva.Image({
        x: 200,
        y: 40,
        image: imageObj,
        draggable: true,
        shadowColor: 'black',
        shadowBlur: 10,
        shadowOffset: 20,
        shadowOpacity: 0.2
      });

      // override color key with black
      lion.colorKey = '#000000';
      Konva.shapes['#000000'] = lion;

      layer.add(lion);

      stage.add(layer);

      lion.cache();

      //document.body.appendChild(lion._cache.canvas.hit._canvas);

      lion.drawHitFromCache();

      layer.draw();

      done();
    };
    imageObj.src = 'assets/lion.png';

    showHit(layer);

    layer.hitCanvas._canvas.style.border = '2px solid black';
  });

  test('test defaults', function() {
    var shape = new Konva.Shape();

    assert.equal(shape.strokeWidth(), 2);
    assert.equal(shape.shadowOffsetX(), 0);
    assert.equal(shape.shadowOffsetY(), 0);
    assert.equal(shape.fillPatternX(), 0);
    assert.equal(shape.fillPatternY(), 0);
    assert.equal(shape.fillRadialGradientStartRadius(), 0);
    assert.equal(shape.fillRadialGradientEndRadius(), 0);
    assert.equal(shape.fillPatternRepeat(), 'repeat');
    assert.equal(shape.fillEnabled(), true);
    assert.equal(shape.strokeEnabled(), true);
    assert.equal(shape.shadowEnabled(), true);
    assert.equal(shape.dashEnabled(), true);
    assert.equal(shape.dashOffset(), 0);
    assert.equal(shape.strokeScaleEnabled(), true);
    assert.equal(shape.fillPriority(), 'color');
    assert.equal(shape.fillPatternOffsetX(), 0);
    assert.equal(shape.fillPatternOffsetY(), 0);
    assert.equal(shape.fillPatternScaleX(), 1);
    assert.equal(shape.fillPatternScaleY(), 1);
    assert.equal(shape.fillLinearGradientStartPointX(), 0);
    assert.equal(shape.fillLinearGradientStartPointY(), 0);
    assert.equal(shape.fillLinearGradientEndPointX(), 0);
    assert.equal(shape.fillLinearGradientEndPointY(), 0);
    assert.equal(shape.fillRadialGradientStartPointX(), 0);
    assert.equal(shape.fillRadialGradientStartPointY(), 0);
    assert.equal(shape.fillRadialGradientEndPointX(), 0);
    assert.equal(shape.fillRadialGradientEndPointY(), 0);
    assert.equal(shape.fillPatternRotation(), 0);
  });

  // ======================================================
  test('hit graph when shape cached before adding to Layer', function() {
    var stage = addStage();
    var layer = new Konva.Layer();
    var rect = new Konva.Rect({
      x: 290,
      y: 111,
      width: 50,
      height: 50,
      fill: 'black'
    });
    rect.cache();

    var click = false;

    rect.on('click', function() {
      click = true;
    });

    layer.add(rect);
    stage.add(layer);

    var top = stage.content.getBoundingClientRect().top;

    showHit(layer);

    stage.simulateMouseDown({
      x: 300,
      y: 120
    });

    stage.simulateMouseUp({
      x: 300,
      y: 120
    });

    //TODO: can't get this to pass
    assert.equal(
      click,
      true,
      'click event should have been fired when mousing down and then up on rect'
    );
  });

  test('class inherence', function() {
    var rect = new Konva.Rect();
    assert.equal(rect instanceof Konva.Rect, true);
    assert.equal(rect instanceof Konva.Shape, true);
    assert.equal(rect instanceof Konva.Node, true);
  });

  test('disable stroke for hit', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 50,
      width: 100,
      height: 50,
      stroke: 'red',
      strokeWidth: 20
    });
    // default value
    assert.equal(rect.strokeHitEnabled(), true);

    rect.strokeHitEnabled(false);
    assert.equal(rect.strokeHitEnabled(), false);
    layer.add(rect);
    stage.add(layer);

    assert.equal(rect.getY(), 50);

    var trace = layer
      .getHitCanvas()
      .getContext()
      .getTrace(true);
    assert.equal(
      trace,
      'clearRect();save();transform();beginPath();rect();closePath();save();fillStyle;fill();restore();restore();'
    );
  });

  test('cache shadow color rgba', function() {
    var circle = new Konva.Circle({
      fill: 'green',
      radius: 50
    });
    // no shadow on start
    assert.equal(circle.hasShadow(), false);
    assert.equal(circle.getShadowRGBA(), undefined);

    // set shadow
    circle.shadowColor('black');
    assert.equal(circle.hasShadow(), true);
    assert.equal(circle.getShadowRGBA(), 'rgba(0,0,0,1)');

    // set another shadow property
    circle.shadowOpacity(0.2);
    assert.equal(circle.getShadowRGBA(), 'rgba(0,0,0,0.2)');

    circle.shadowColor('rgba(10,10,10,0.5)');
    assert.equal(circle.getShadowRGBA(), 'rgba(10,10,10,0.1)');

    // reset shadow
    circle.shadowColor(null);
    assert.equal(circle.getShadowRGBA(), undefined);
  });

  test('scale should also effect shadow offset', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 100,
      width: 100,
      height: 100,
      scaleX: 0.5,
      scaleY: 0.5,
      fill: 'green',
      shadowColor: 'black',
      shadowBlur: 0,
      shadowOffset: { x: 10, y: 10 }
    });

    layer.add(rect);
    stage.add(layer);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');
    // rect
    context.beginPath();
    context.rect(100, 100, 50, 50);
    context.closePath();

    context.fillStyle = 'green';
    context.shadowColor = 'rgba(0,0,0,1)';
    context.shadowBlur = 0;
    context.shadowOffsetX = 5 * canvas.ratio;
    context.shadowOffsetY = 5 * canvas.ratio;
    context.fill();

    compareLayerAndCanvas(layer, canvas, 10);

    var trace = layer.getContext().getTrace();

    assert.equal(
      trace,
      'clearRect(0,0,578,200);save();transform(0.5,0,0,0.5,100,100);save();shadowColor=rgba(0,0,0,1);shadowBlur=0;shadowOffsetX=5;shadowOffsetY=5;beginPath();rect(0,0,100,100);closePath();fillStyle=green;fill();restore();restore();'
    );
  });

  test('scale should also effect shadow offset - negative scale', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 100,
      width: 100,
      height: 100,
      scaleX: -0.5,
      scaleY: 0.5,
      fill: 'green',
      shadowColor: 'black',
      shadowBlur: 10,
      shadowOffset: { x: 10, y: 10 }
    });

    layer.add(rect);
    stage.add(layer);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');
    // rect
    context.beginPath();
    context.rect(50, 100, 50, 50);
    context.closePath();

    context.fillStyle = 'green';
    context.shadowColor = 'rgba(0,0,0,1)';
    context.shadowBlur = 10;
    context.shadowOffsetX = -5 * canvas.ratio;
    context.shadowOffsetY = 5 * canvas.ratio;
    context.fill();

    compareLayerAndCanvas(layer, canvas, 150);

    // var trace = layer.getContext().getTrace();

    // assert.equal(
    //   trace,
    //   'clearRect(0,0,578,200);save();transform(-0.5,0,0,0.5,100,100);save();shadowColor=rgba(0,0,0,1);shadowBlur=10;shadowOffsetX=-5;shadowOffsetY=5;beginPath();rect(0,0,100,100);closePath();fillStyle=green;fill();restore();restore();'
    // );
  });

  test('scale of parent container should also effect shadow offset', function() {
    var stage = addStage();

    var layer = new Konva.Layer();
    var group = new Konva.Group({
      x: 100,
      y: 100,
      scaleX: 0.5,
      scaleY: 0.5
    });
    var rect = new Konva.Rect({
      width: 200,
      height: 200,
      scaleX: 0.5,
      scaleY: 0.5,
      fill: 'green',
      shadowColor: 'black',
      shadowBlur: 0,
      shadowOffset: { x: 20, y: 20 }
    });

    group.add(rect);
    layer.add(group);
    stage.add(layer);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');
    // rect
    context.beginPath();
    context.rect(100, 100, 50, 50);
    context.closePath();

    context.fillStyle = 'green';
    context.shadowColor = 'rgba(0,0,0,1)';
    context.shadowBlur = 0;
    context.shadowOffsetX = 5 * canvas.ratio;
    context.shadowOffsetY = 5 * canvas.ratio;
    context.fill();

    compareLayerAndCanvas(layer, canvas, 10);

    var trace = layer.getContext().getTrace();

    assert.equal(
      trace,
      'clearRect(0,0,578,200);save();transform(0.25,0,0,0.25,100,100);save();shadowColor=rgba(0,0,0,1);shadowBlur=0;shadowOffsetX=5;shadowOffsetY=5;beginPath();rect(0,0,200,200);closePath();fillStyle=green;fill();restore();restore();'
    );
  });

  test('optional disable buffer canvas', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 50,
      width: 100,
      height: 50,
      fill: 'green',
      stroke: 'black',
      strokeWidth: 10,
      opacity: 0.5,
      perfectDrawEnabled: false
    });

    layer.add(rect);

    stage.add(layer);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');
    context.globalAlpha = 0.5;
    // stroke
    context.beginPath();
    context.rect(100, 50, 100, 50);
    context.closePath();
    context.lineWidth = 10;
    context.strokeStyle = 'black';
    context.fillStyle = 'green';
    context.fill();
    context.stroke();

    compareLayerAndCanvas(layer, canvas, 10);

    var trace = layer.getContext().getTrace();

    assert.equal(
      trace,
      'clearRect(0,0,578,200);save();transform(1,0,0,1,100,50);globalAlpha=0.5;beginPath();rect(0,0,100,50);closePath();fillStyle=green;fill();lineWidth=10;strokeStyle=black;stroke();restore();'
    );
  });

  // ======================================================
  test('optional disable shadow for stroke', function() {
    var stage = addStage();

    var layer = new Konva.Layer();

    var rect = new Konva.Rect({
      x: 100,
      y: 50,
      width: 100,
      height: 50,
      fill: 'green',
      stroke: 'black',
      strokeWidth: 10,
      shadowColor: 'grey',
      shadowBlur: 10,
      shadowOffset: {
        x: 20,
        y: 20
      },
      shadowForStrokeEnabled: false
    });

    layer.add(rect);
    stage.add(layer);

    var canvas = createCanvas();
    var context = canvas.getContext('2d');
    context.beginPath();
    context.rect(100, 50, 100, 50);
    context.closePath();
    context.fillStyle = 'green';
    context.shadowColor = 'grey';
    context.shadowBlur = 10 * canvas.ratio;
    context.shadowOffsetX = 20 * canvas.ratio;
    context.shadowOffsetY = 20 * canvas.ratio;
    context.lineWidth = 10;
    context.fill();

    context.shadowColor = 'rgba(0,0,0,0)';
    context.stroke();

    compareLayerAndCanvas(layer, canvas, 10);

    var trace = layer.getContext().getTrace();
    assert.equal(
      trace,
      'clearRect(0,0,578,200);save();transform(1,0,0,1,100,50);save();shadowColor=rgba(128,128,128,1);shadowBlur=10;shadowOffsetX=20;shadowOffsetY=20;beginPath();rect(0,0,100,50);closePath();fillStyle=green;fill();lineWidth=10;shadowColor=rgba(0,0,0,0);strokeStyle=black;stroke();restore();restore();'
    );
  });

  test('clone custom shape', function() {
    var className = 'myCustomName';
    var CustomShape = function() {
      CustomShape.super.apply(this, arguments);
      this.className = className;
    };

    CustomShape.prototype.foo = function() {};

    Konva.Util.extend(CustomShape, Konva.Shape);

    var myShape = new CustomShape({
      fill: 'grey'
    });

    var clone = myShape.clone();
    assert.equal(clone instanceof CustomShape, true);
    assert.equal(clone instanceof Konva.Shape, true);
    assert.equal(clone.className, className);
    assert.equal(clone.fill(), 'grey');
    assert.equal(clone.foo, CustomShape.prototype.foo);
  });

  test('getClientRect should skip disabled attributes', function() {
    var stage = addStage();
    var layer = new Konva.Layer();
    var shape = new Konva.Rect({
      x: 200,
      y: 100,
      width: 100,
      height: 100,
      fill: 'green',
      stroke: 'black',
      strokeWidth: 4,
      strokeEnabled: false,
      shadowOffsetX: 10,
      shadowEnabled: false
    });

    layer.add(shape);
    stage.add(layer);

    var rect = shape.getClientRect();

    assert.equal(rect.width, 100, 'should not effect width');
    assert.equal(rect.height, 100, 'should not effect width');
  });

  test('getClientRect for shape in transformed parent', function() {
    var stage = addStage();
    var layer = new Konva.Layer();
    stage.add(layer);

    var group = new Konva.Group({
      x: 110,
      y: 0,
      rotation: 90
    });
    layer.add(group);

    var shape = new Konva.Rect({
      x: 0,
      y: 0,
      width: 100,
      height: 100,
      fill: 'green'
    });
    group.add(shape);

    var relativeRect = shape.getClientRect({ relativeTo: group });

    assert.equal(relativeRect.x, 0);
    assert.equal(relativeRect.y, 0);
    assert.equal(relativeRect.width, 100);
    assert.equal(relativeRect.height, 100);

    var absRect = shape.getClientRect();

    assert.equal(absRect.x, 10);
    assert.equal(absRect.y, 0);
    assert.equal(absRect.width, 100);
    assert.equal(absRect.height, 100);
  });

  test('shadow should respect pixel ratio', function() {
    var stage = addStage();
    var layer = new Konva.Layer();
    layer.getCanvas().setPixelRatio(2);
    var shape = new Konva.Rect({
      width: 100,
      height: 100,
      fill: 'black',
      shadowColor: 'green',
      shadowOffsetX: 20,
      shadowOffsetY: 20,
      shadowBlur: 0
    });

    layer.add(shape);
    stage.add(layer);
    var data = layer.getContext().getImageData(15 * 2, (100 + 5) * 2, 1, 1);
    assert.equal(data.data[3], 0, 'pixel should be empty, no shadow here');
  });

  test('text shadow blur should take scale into account', function() {
    var stage = addStage();
    var layer1 = new Konva.Layer();
    stage.add(layer1);

    var rect1 = new Konva.Rect({
      x: 10,
      y: 10,
      scaleX: 0.5,
      scaleY: 0.5,
      width: 80,
      height: 80,
      fill: 'black',
      shadowColor: 'black',
      shadowOffsetX: 0,
      shadowOffsetY: 50,
      shadowBlur: 10
    });
    layer1.add(rect1);
    stage.add(layer1);

    var layer2 = new Konva.Layer();
    stage.add(layer2);

    var rect2 = new Konva.Rect({
      x: 10,
      y: 10,
      fill: 'black',
      width: 40,
      height: 40,
      shadowColor: 'black',
      shadowOffsetX: 0,
      shadowOffsetY: 25,
      shadowBlur: 5
    });
    layer2.add(rect2);
    stage.add(layer2);

    if (!window.isPhantomJS) {
      compareLayers(layer1, layer2, 30);
    }
  });

  // ======================================================
  test('sceneFunc and hitFunc should have shape as second argument', function() {
    var stage = addStage();
    var layer = new Konva.Layer();
    var shape = new Konva.Shape({
      sceneFunc: function(context, shape) {
        assert.equal(this, shape);
        context.beginPath();
        context.moveTo(0, 0);
        context.lineTo(100, 0);
        context.lineTo(100, 100);
        context.closePath();
        context.fillStrokeShape(shape);
      },
      x: 200,
      y: 100,
      fill: 'green',
      stroke: 'blue',
      strokeWidth: 5
    });
    layer.add(shape);

    var rect = new Konva.Rect({
      hitFunc: function(ctx, shape) {
        assert.equal(this, shape);
      }
    });
    layer.add(rect);
    stage.add(layer);
  });
});