// Copyright 2008 The Closure Library Authors. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

/**
 * @fileoverview Unit tests for goog.position.
 *
 * @author eae@google.com (Emil A Eklund)
 */

/** @suppress {extraProvide} */
goog.provide('goog.positioningTest');

goog.require('goog.dom');
goog.require('goog.dom.DomHelper');
goog.require('goog.dom.TagName');
goog.require('goog.labs.userAgent.browser');
goog.require('goog.math.Box');
goog.require('goog.math.Coordinate');
goog.require('goog.math.Size');
goog.require('goog.positioning');
goog.require('goog.positioning.Corner');
goog.require('goog.positioning.Overflow');
goog.require('goog.positioning.OverflowStatus');
goog.require('goog.style');
goog.require('goog.testing.ExpectedFailures');
goog.require('goog.testing.jsunit');
goog.require('goog.userAgent');
goog.require('goog.userAgent.product');

goog.setTestOnly('goog.positioningTest');

// Allow positions to be off by one in gecko as it reports scrolling
// offsets in steps of 2.  Otherwise, allow for subpixel difference
// as seen in IE10+
var ALLOWED_OFFSET = goog.userAgent.GECKO ? 1 : 0.1;
// Error bar for positions since some browsers are not super accurate
// in reporting them.
var EPSILON = 2;

var expectedFailures = new goog.testing.ExpectedFailures();

var corner = goog.positioning.Corner;
var overflow = goog.positioning.Overflow;
var testArea;

function setUp() {
  window.scrollTo(0, 0);

  var viewportSize = goog.dom.getViewportSize();
  // Some tests need enough size viewport.
  if (viewportSize.width < 600 || viewportSize.height < 600) {
    window.moveTo(0, 0);
    window.resizeTo(640, 640);
  }

  testArea = goog.dom.getElement('test-area');
}

function tearDown() {
  expectedFailures.handleTearDown();
  testArea.setAttribute('style', '');
  goog.dom.removeChildren(testArea);
}


/**
 * This is used to round pixel values on FF3 Mac.
 */
function assertRoundedEquals(a, b, c) {
  function round(x) {
    return goog.userAgent.GECKO && (goog.userAgent.MAC || goog.userAgent.X11) &&
            goog.userAgent.isVersionOrHigher('1.9') ?
        Math.round(x) :
        x;
  }
  if (arguments.length == 3) {
    assertRoughlyEquals(a, round(b), round(c), ALLOWED_OFFSET);
  } else {
    assertRoughlyEquals(round(a), round(b), ALLOWED_OFFSET);
  }
}

function testPositionAtAnchorLeftToRight() {
  var anchor = document.getElementById('anchor1');
  var popup = document.getElementById('popup1');

  // Anchor top left to top left.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT);
  var anchorRect = goog.style.getBounds(anchor);
  var popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Left edge of popup should line up with left edge ' +
          'of anchor.',
      anchorRect.left, popupRect.left);
  assertRoundedEquals(
      'Popup should have the same y position as the anchor.', anchorRect.top,
      popupRect.top);

  // Anchor top left to bottom left.
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_LEFT, popup, corner.TOP_LEFT);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Left edge of popup should line up with left edge ' +
          'of anchor.',
      anchorRect.left, popupRect.left);
  assertRoundedEquals(
      'Popup should be positioned just below the anchor.',
      anchorRect.top + anchorRect.height, popupRect.top);

  // Anchor top left to top right.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_RIGHT, popup, corner.TOP_LEFT);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Popup should be positioned just right of the anchor.',
      anchorRect.left + anchorRect.width, popupRect.left);
  assertRoundedEquals(
      'Popup should have the same y position as the anchor.', anchorRect.top,
      popupRect.top);

  // Anchor top right to bottom right.
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_RIGHT, popup, corner.TOP_RIGHT);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Right edge of popup should line up with right edge ' +
          'of anchor.',
      anchorRect.left + anchorRect.width, popupRect.left + popupRect.width);
  assertRoundedEquals(
      'Popup should be positioned just below the anchor.',
      anchorRect.top + anchorRect.height, popupRect.top);

  // Anchor top start to bottom start.
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_START, popup, corner.TOP_START);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Left edge of popup should line up with left edge ' +
          'of anchor.',
      anchorRect.left, popupRect.left);
  assertRoundedEquals(
      'Popup should be positioned just below the anchor.',
      anchorRect.top + anchorRect.height, popupRect.top);

  // Anchor top center to top center.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_CENTER, popup, corner.TOP_CENTER);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  // We use flooring math because we consider 10 and 10.5 to be "equal".
  // IE8 doesn't allow split pixels in positioning and as such this test would
  // fail on it, as Anchor L+W/2 is round while Popup L+W/2 is .5 away.
  assertRoundedEquals(
      'The center of popup should line up with the center ' +
          'of anchor.',
      Math.floor(anchorRect.left + anchorRect.width / 2),
      Math.floor(popupRect.left + popupRect.width / 2));
  assertRoundedEquals(
      'Popup should have the same y position as the anchor.', anchorRect.top,
      popupRect.top);

  // Anchor top center to top left.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_CENTER, popup, corner.TOP_LEFT);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Left edge of popup should line up with the center ' +
          'of anchor.',
      Math.floor(anchorRect.left + anchorRect.width / 2),
      Math.floor(popupRect.left));
  assertRoundedEquals(
      'Popup should have the same y position as the anchor.', anchorRect.top,
      popupRect.top);

  // Anchor bottom center to top left.
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_CENTER, popup, corner.TOP_LEFT);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Left edge of popup should line up with the middle ' +
          'of anchor.',
      Math.floor(anchorRect.left + anchorRect.width / 2),
      Math.floor(popupRect.left));
  assertRoundedEquals(
      'Popup should be positioned just below the anchor.',
      anchorRect.top + anchorRect.height, popupRect.top);

  // Anchor bottom left to top center.
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_LEFT, popup, corner.TOP_CENTER);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Left edge of popup should line up with the middle ' +
          'of anchor.',
      anchorRect.left, Math.floor(popupRect.left + popupRect.width / 2));
  assertRoundedEquals(
      'Popup should be positioned just below the anchor.',
      anchorRect.top + anchorRect.height, popupRect.top);
}


function testPositionAtAnchorWithOffset() {
  var anchor = document.getElementById('anchor1');
  var popup = document.getElementById('popup1');

  // Anchor top left to top left with an offset moving the popup away from the
  // anchor.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, newCoord(-15, -20));
  var anchorRect = goog.style.getBounds(anchor);
  var popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Left edge of popup should be fifteen pixels from ' +
          'anchor.',
      anchorRect.left, popupRect.left + 15);
  assertRoundedEquals(
      'Top edge of popup should be twenty pixels from anchor.', anchorRect.top,
      popupRect.top + 20);

  // Anchor top left to top left with an offset moving the popup towards the
  // anchor.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, newCoord(3, 1));
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Left edge of popup should be three pixels right of ' +
          'the anchor\'s left edge',
      anchorRect.left, popupRect.left - 3);
  assertRoundedEquals(
      'Top edge of popup should be one pixel below of the ' +
          'anchor\'s top edge',
      anchorRect.top, popupRect.top - 1);
}


function testPositionAtAnchorOverflowLeftEdgeRightToLeft() {
  var anchor = document.getElementById('anchor5');
  var popup = document.getElementById('popup5');

  var status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, undefined, undefined,
      overflow.FAIL_X);
  assertFalse(
      'Positioning operation should have failed.',
      (status & goog.positioning.OverflowStatus.FAILED) == 0);

  // Change overflow strategy to ADJUST.
  status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, undefined, undefined,
      overflow.ADJUST_X);

  // Fails in Chrome because of infrastructure issues, temporarily disabled.
  // See b/4274723.
  expectedFailures.expectFailureFor(goog.userAgent.product.CHROME);
  try {
    assertTrue(
        'Positioning operation should have been successful.',
        (status & goog.positioning.OverflowStatus.FAILED) == 0);
    assertTrue(
        'Positioning operation should have been adjusted.',
        (status & goog.positioning.OverflowStatus.ADJUSTED_X) != 0);
  } catch (e) {
    expectedFailures.handleException(e);
  }

  var anchorRect = goog.style.getBounds(anchor);
  var popupRect = goog.style.getBounds(popup);
  var parentRect = goog.style.getBounds(anchor.parentNode);
  assertTrue(
      'Position should have been adjusted so that the left edge of ' +
          'the popup is left of the anchor but still within the bounding ' +
          'box of the parent container.',
      anchorRect.left <= popupRect.left <= parentRect.left);
}


function testPositionAtAnchorWithMargin() {
  var anchor = document.getElementById('anchor1');
  var popup = document.getElementById('popup1');

  // Anchor top left to top left.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, undefined,
      new goog.math.Box(1, 2, 3, 4));
  var anchorRect = goog.style.getBounds(anchor);
  var popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Left edge of popup should be four pixels from anchor.', anchorRect.left,
      popupRect.left - 4);
  assertRoundedEquals(
      'Top edge of popup should be one pixels from anchor.', anchorRect.top,
      popupRect.top - 1);

  // Anchor top right to bottom right.
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_RIGHT, popup, corner.TOP_RIGHT, undefined,
      new goog.math.Box(1, 2, 3, 4));
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);

  var visibleAnchorRect = goog.positioning.getVisiblePart_(anchor);

  assertRoundedEquals(
      'Right edge of popup should line up with right edge ' +
          'of anchor.',
      visibleAnchorRect.left + visibleAnchorRect.width,
      popupRect.left + popupRect.width + 2);

  assertRoundedEquals(
      'Popup should be positioned just below the anchor.',
      visibleAnchorRect.top + visibleAnchorRect.height, popupRect.top - 1);
}


function testPositionAtAnchorRightToLeft() {
  if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('6')) {
    // These tests fails with IE6.
    // TODO(user): Investigate the reason.
    return;
  }
  if (goog.userAgent.product.SAFARI) {
    // TODO(b/20733468): Disabled so we can get the rest of the Closure test
    // suite running in a continuous build. Will investigate later.
    return;
  }

  var anchor = document.getElementById('anchor2');
  var popup = document.getElementById('popup2');

  // Anchor top left to top left.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT);
  var anchorRect = goog.style.getBounds(anchor);
  var popupRect = goog.style.getBounds(popup);

  assertRoundedEquals(
      'Left edge of popup should line up with left edge ' +
          'of anchor.',
      anchorRect.left, popupRect.left);
  assertRoundedEquals(
      'Popup should have the same y position as the anchor.', anchorRect.top,
      popupRect.top);

  // Anchor top start to bottom start.
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_START, popup, corner.TOP_START);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Right edge of popup should line up with right edge ' +
          'of anchor.',
      anchorRect.left + anchorRect.width, popupRect.left + popupRect.width);
  assertRoundedEquals(
      'Popup should be positioned just below the anchor.',
      anchorRect.top + anchorRect.height, popupRect.top);
}

function testPositionAtAnchorRightToLeftWithScroll() {
  if (goog.userAgent.IE && goog.userAgent.isVersionOrHigher('6')) {
    // These tests fails with IE6.
    // TODO(user): Investigate the reason.
    return;
  }
  if (goog.userAgent.product.SAFARI) {
    // TODO(b/20733468): Disabled so we can get the rest of the Closure test
    // suite running in a continuous build. Will investigate later.
    return;
  }

  var anchor = document.getElementById('anchor8');
  var popup = document.getElementById('popup8');

  // Anchor top left to top left.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT);
  var anchorRect = goog.style.getBounds(anchor);
  var popupRect = goog.style.getBounds(popup);

  // TODO(joeltine): Chrome 47 has issues with RTL scroll positioning. Remove
  // chrome check when
  // https://code.google.com/p/chromium/issues/detail?id=568706 is resolved.
  if (!goog.labs.userAgent.browser.isChrome()) {
    assertRoundedEquals(
        'Left edge of popup should line up with left edge ' +
            'of anchor.',
        anchorRect.left, popupRect.left);
  }
  assertRoundedEquals(
      'Popup should have the same y position as the anchor.', anchorRect.top,
      popupRect.top);

  // Anchor top start to bottom start.
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_START, popup, corner.TOP_START);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);

  var visibleAnchorRect = goog.positioning.getVisiblePart_(anchor);
  var visibleAnchorBox = visibleAnchorRect.toBox();

  // TODO(joeltine): Chrome 47 has issues with RTL scroll positioning. Remove
  // chrome check when
  // https://code.google.com/p/chromium/issues/detail?id=568706 is resolved.
  if (!goog.labs.userAgent.browser.isChrome()) {
    assertRoundedEquals(
        'Right edge of popup should line up with right edge ' +
            'of anchor.',
        anchorRect.left + anchorRect.width, popupRect.left + popupRect.width);
  }
  assertRoundedEquals(
      'Popup should be positioned just below the anchor.',
      visibleAnchorBox.bottom, popupRect.top);
}

function testPositionAtAnchorBodyViewport() {
  var anchor = document.getElementById('anchor1');
  var popup = document.getElementById('popup3');

  // Anchor top left to top left.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT);
  var anchorRect = goog.style.getBounds(anchor);
  var popupRect = goog.style.getBounds(popup);
  assertEquals(
      'Left edge of popup should line up with left edge of anchor.',
      anchorRect.left, popupRect.left);
  assertRoughlyEquals(
      'Popup should have the same y position as the anchor.', anchorRect.top,
      popupRect.top, 1);

  // Anchor top start to bottom right.
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_RIGHT, popup, corner.TOP_RIGHT);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertEquals(
      'Right edge of popup should line up with right edge of anchor.',
      anchorRect.left + anchorRect.width, popupRect.left + popupRect.width);
  assertRoughlyEquals(
      'Popup should be positioned just below the anchor.',
      anchorRect.top + anchorRect.height, popupRect.top, 1);

  // Anchor top right to top left.
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertEquals(
      'Right edge of popup should line up with left edge of anchor.',
      anchorRect.left, popupRect.left + popupRect.width);
  assertRoughlyEquals(
      'Popup should have the same y position as the anchor.', anchorRect.top,
      popupRect.top, 1);
}

function testPositionAtAnchorSpecificViewport() {
  var anchor = document.getElementById('anchor1');
  var popup = document.getElementById('popup3');

  // Anchor top right to top left within outerbox.
  var status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, undefined, undefined,
      overflow.FAIL_X);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertTrue(
      'Positioning operation should have been successful.',
      (status & goog.positioning.OverflowStatus.FAILED) == 0);
  assertTrue(
      'X position should not have been adjusted.',
      (status & goog.positioning.OverflowStatus.ADJUSTED_X) == 0);
  assertTrue(
      'Y position should not have been adjusted.',
      (status & goog.positioning.OverflowStatus.ADJUSTED_Y) == 0);
  assertEquals(
      'Right edge of popup should line up with left edge of anchor.',
      anchorRect.left, popupRect.left + popupRect.width);
  assertRoughlyEquals(
      'Popup should have the same y position as the anchor.', anchorRect.top,
      popupRect.top, 1);

  // position again within box1.
  var box = document.getElementById('box1');
  var viewport = goog.style.getBounds(box);
  status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, undefined, undefined,
      overflow.FAIL_X, undefined, viewport);
  assertFalse(
      'Positioning operation should have failed.',
      (status & goog.positioning.OverflowStatus.FAILED) == 0);

  // Change overflow strategy to adjust.
  status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, undefined, undefined,
      overflow.ADJUST_X, undefined, viewport);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertTrue(
      'Positioning operation should have been successful.',
      (status & goog.positioning.OverflowStatus.FAILED) == 0);
  assertFalse(
      'X position should have been adjusted.',
      (status & goog.positioning.OverflowStatus.ADJUSTED_X) == 0);
  assertTrue(
      'Y position should not have been adjusted.',
      (status & goog.positioning.OverflowStatus.ADJUSTED_Y) == 0);
  assertRoughlyEquals(
      'Left edge of popup should line up with left edge of viewport.',
      viewport.left, popupRect.left, EPSILON);
  assertRoughlyEquals(
      'Popup should have the same y position as the anchor.', anchorRect.top,
      popupRect.top, 1);
}

function testPositionAtAnchorOutsideViewport() {
  var anchor = document.getElementById('anchor4');
  var popup = document.getElementById('popup1');

  var status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT);
  var anchorRect = goog.style.getBounds(anchor);
  var popupRect = goog.style.getBounds(popup);
  assertTrue(
      'Positioning operation should have been successful.',
      (status & goog.positioning.OverflowStatus.FAILED) == 0);
  assertTrue(
      'X position should not have been adjusted.',
      (status & goog.positioning.OverflowStatus.ADJUSTED_X) == 0);
  assertTrue(
      'Y position should not have been adjusted.',
      (status & goog.positioning.OverflowStatus.ADJUSTED_Y) == 0);

  assertEquals(
      'Right edge of popup should line up with left edge of anchor.',
      anchorRect.left, popupRect.left + popupRect.width);

  // Change overflow strategy to fail.
  status = goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_RIGHT, popup, corner.TOP_RIGHT, undefined,
      undefined, overflow.FAIL_X);
  assertFalse(
      'Positioning operation should have failed.',
      (status & goog.positioning.OverflowStatus.FAILED) == 0);

  // Change overflow strategy to adjust.
  status = goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_RIGHT, popup, corner.TOP_RIGHT, undefined,
      undefined, overflow.ADJUST_X);
  anchorRect = goog.style.getBounds(anchor);
  popupRect = goog.style.getBounds(popup);
  assertTrue(
      'Positioning operation should have been successful.',
      (status & goog.positioning.OverflowStatus.FAILED) == 0);
  assertFalse(
      'X position should have been adjusted.',
      (status & goog.positioning.OverflowStatus.ADJUSTED_X) == 0);
  assertTrue(
      'Y position should not have been adjusted.',
      (status & goog.positioning.OverflowStatus.ADJUSTED_Y) == 0);
  assertRoughlyEquals(
      'Left edge of popup should line up with left edge of viewport.', 0,
      popupRect.left, EPSILON);
  assertEquals(
      'Popup should be positioned just below the anchor.',
      anchorRect.top + anchorRect.height, popupRect.top);
}


function testAdjustForViewportFailIgnore() {
  var f = goog.positioning.adjustForViewport_;
  var viewport = new goog.math.Box(100, 200, 200, 100);
  var overflow = goog.positioning.Overflow.IGNORE;

  var pos = newCoord(150, 150);
  var size = newSize(50, 50);
  assertEquals(
      'Viewport overflow should be ignored.',
      goog.positioning.OverflowStatus.NONE, f(pos, size, viewport, overflow));

  pos = newCoord(150, 150);
  size = newSize(100, 50);
  assertEquals(
      'Viewport overflow should be ignored.',
      goog.positioning.OverflowStatus.NONE, f(pos, size, viewport, overflow));

  pos = newCoord(50, 50);
  size = newSize(50, 50);
  assertEquals(
      'Viewport overflow should be ignored.',
      goog.positioning.OverflowStatus.NONE, f(pos, size, viewport, overflow));
}


function testAdjustForViewportFailXY() {
  var f = goog.positioning.adjustForViewport_;
  var viewport = new goog.math.Box(100, 200, 200, 100);
  var overflow =
      goog.positioning.Overflow.FAIL_X | goog.positioning.Overflow.FAIL_Y;

  var pos = newCoord(150, 150);
  var size = newSize(50, 50);
  assertEquals(
      'Element should not overflow viewport.',
      goog.positioning.OverflowStatus.NONE, f(pos, size, viewport, overflow));

  pos = newCoord(150, 150);
  size = newSize(100, 50);
  assertEquals(
      'Element should overflow the right edge of viewport.',
      goog.positioning.OverflowStatus.FAILED_RIGHT,
      f(pos, size, viewport, overflow));

  pos = newCoord(150, 150);
  size = newSize(50, 100);
  assertEquals(
      'Element should overflow the bottom edge of viewport.',
      goog.positioning.OverflowStatus.FAILED_BOTTOM,
      f(pos, size, viewport, overflow));

  pos = newCoord(50, 150);
  size = newSize(50, 50);
  assertEquals(
      'Element should overflow the left edge of viewport.',
      goog.positioning.OverflowStatus.FAILED_LEFT,
      f(pos, size, viewport, overflow));

  pos = newCoord(150, 50);
  size = newSize(50, 50);
  assertEquals(
      'Element should overflow the top edge of viewport.',
      goog.positioning.OverflowStatus.FAILED_TOP,
      f(pos, size, viewport, overflow));

  pos = newCoord(50, 50);
  size = newSize(50, 50);
  assertEquals(
      'Element should overflow the left & top edges of viewport.',
      goog.positioning.OverflowStatus.FAILED_LEFT |
          goog.positioning.OverflowStatus.FAILED_TOP,
      f(pos, size, viewport, overflow));
}


function testAdjustForViewportAdjustXFailY() {
  var f = goog.positioning.adjustForViewport_;
  var viewport = new goog.math.Box(100, 200, 200, 100);
  var overflow =
      goog.positioning.Overflow.ADJUST_X | goog.positioning.Overflow.FAIL_Y;

  var pos = newCoord(150, 150);
  var size = newSize(50, 50);
  assertEquals(
      'Element should not overflow viewport.',
      goog.positioning.OverflowStatus.NONE, f(pos, size, viewport, overflow));
  assertEquals('X Position should not have been changed.', 150, pos.x);
  assertEquals('Y Position should not have been changed.', 150, pos.y);

  pos = newCoord(150, 150);
  size = newSize(100, 50);
  assertEquals(
      'Element position should be adjusted not to overflow right ' +
          'edge of viewport.',
      goog.positioning.OverflowStatus.ADJUSTED_X,
      f(pos, size, viewport, overflow));
  assertEquals('X Position should be adjusted to 100.', 100, pos.x);
  assertEquals('Y Position should not have been changed.', 150, pos.y);

  pos = newCoord(50, 150);
  size = newSize(100, 50);
  assertEquals(
      'Element position should be adjusted not to overflow left ' +
          'edge of viewport.',
      goog.positioning.OverflowStatus.ADJUSTED_X,
      f(pos, size, viewport, overflow));
  assertEquals('X Position should be adjusted to 100.', 100, pos.x);
  assertEquals('Y Position should not have been changed.', 150, pos.y);


  pos = newCoord(50, 50);
  size = newSize(100, 50);
  assertEquals(
      'Element position should be adjusted not to overflow left ' +
          'edge of viewport, should overflow bottom edge.',
      goog.positioning.OverflowStatus.ADJUSTED_X |
          goog.positioning.OverflowStatus.FAILED_TOP,
      f(pos, size, viewport, overflow));
  assertEquals('X Position should be adjusted to 100.', 100, pos.x);
  assertEquals('Y Position should not have been changed.', 50, pos.y);
}


function testAdjustForViewportResizeHeight() {
  var f = goog.positioning.adjustForViewport_;
  var viewport = new goog.math.Box(0, 200, 200, 0);
  var overflow = goog.positioning.Overflow.RESIZE_HEIGHT;

  var pos = newCoord(150, 150);
  var size = newSize(25, 100);
  assertEquals(
      'Viewport height should be resized.',
      goog.positioning.OverflowStatus.HEIGHT_ADJUSTED,
      f(pos, size, viewport, overflow));
  assertEquals('Height should be resized to 50.', 50, size.height);
  assertTrue(
      'Output box is within viewport',
      viewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));

  var pos = newCoord(0, 0);
  var size = newSize(50, 250);
  assertEquals(
      'Viewport height should be resized.',
      goog.positioning.OverflowStatus.HEIGHT_ADJUSTED,
      f(pos, size, viewport, overflow));
  assertEquals('Height should be resized to 200.', 200, size.height);
  assertTrue(
      'Output box is within viewport',
      viewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));

  var pos = newCoord(0, -50);
  var size = newSize(50, 240);
  assertEquals(
      'Viewport height should be resized.',
      goog.positioning.OverflowStatus.HEIGHT_ADJUSTED,
      f(pos, size, viewport, overflow));
  assertEquals('Height should be resized to 190.', 190, size.height);
  assertTrue(
      'Output box is within viewport',
      viewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));

  var pos = newCoord(0, -50);
  var size = newSize(50, 300);
  assertEquals(
      'Viewport height should be resized.',
      goog.positioning.OverflowStatus.HEIGHT_ADJUSTED,
      f(pos, size, viewport, overflow));
  assertEquals('Height should be resized to 200.', 200, size.height);
  assertTrue(
      'Output box is within viewport',
      viewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));

  pos = newCoord(150, 150);
  size = newSize(50, 50);
  assertEquals(
      'No Viewport overflow.', goog.positioning.OverflowStatus.NONE,
      f(pos, size, viewport, overflow));
  assertTrue(
      'Output box is within viewport',
      viewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));

  var offsetViewport = new goog.math.Box(100, 200, 300, 0);
  var pos = newCoord(0, 50);
  var size = newSize(50, 240);
  assertEquals(
      'Viewport height should be resized.',
      goog.positioning.OverflowStatus.HEIGHT_ADJUSTED,
      f(pos, size, offsetViewport, overflow));
  assertEquals('Height should be resized to 190.', 190, size.height);
  assertTrue(
      'Output box is within viewport',
      offsetViewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));
}



function testAdjustForViewportResizeWidth() {
  var f = goog.positioning.adjustForViewport_;
  var viewport = new goog.math.Box(0, 200, 200, 0);
  var overflow = goog.positioning.Overflow.RESIZE_WIDTH;

  var pos = newCoord(150, 150);
  var size = newSize(100, 25);
  assertEquals(
      'Viewport width should be resized.',
      goog.positioning.OverflowStatus.WIDTH_ADJUSTED,
      f(pos, size, viewport, overflow));
  assertEquals('Width should be resized to 50.', 50, size.width);
  assertTrue(
      'Output box is within viewport',
      viewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));

  var pos = newCoord(0, 0);
  var size = newSize(250, 50);
  assertEquals(
      'Viewport width should be resized.',
      goog.positioning.OverflowStatus.WIDTH_ADJUSTED,
      f(pos, size, viewport, overflow));
  assertEquals('Width should be resized to 200.', 200, size.width);
  assertTrue(
      'Output box is within viewport',
      viewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));

  var pos = newCoord(-50, 0);
  var size = newSize(240, 50);
  assertEquals(
      'Viewport width should be resized.',
      goog.positioning.OverflowStatus.WIDTH_ADJUSTED,
      f(pos, size, viewport, overflow));
  assertEquals('Width should be resized to 190.', 190, size.width);
  assertTrue(
      'Output box is within viewport',
      viewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));

  var pos = newCoord(-50, 0);
  var size = newSize(300, 50);
  assertEquals(
      'Viewport width should be resized.',
      goog.positioning.OverflowStatus.WIDTH_ADJUSTED,
      f(pos, size, viewport, overflow));
  assertEquals('Width should be resized to 200.', 200, size.width);
  assertTrue(
      'Output box is within viewport',
      viewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));

  pos = newCoord(150, 150);
  size = newSize(50, 50);
  assertEquals(
      'No Viewport overflow.', goog.positioning.OverflowStatus.NONE,
      f(pos, size, viewport, overflow));
  assertTrue(
      'Output box is within viewport',
      viewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));

  var offsetViewport = new goog.math.Box(0, 300, 200, 100);
  var pos = newCoord(50, 0);
  var size = newSize(240, 50);
  assertEquals(
      'Viewport width should be resized.',
      goog.positioning.OverflowStatus.WIDTH_ADJUSTED,
      f(pos, size, offsetViewport, overflow));
  assertEquals('Width should be resized to 190.', 190, size.width);
  assertTrue(
      'Output box is within viewport',
      offsetViewport.contains(
          new goog.math.Box(
              pos.y, pos.x + size.width, pos.y + size.height, pos.x)));
}


function testPositionAtAnchorWithResizeHeight() {
  var anchor = document.getElementById('anchor9');
  var popup = document.getElementById('popup9');
  var box = document.getElementById('box9');
  var viewport = goog.style.getBounds(box);

  var status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_START, popup, corner.TOP_START,
      new goog.math.Coordinate(0, -20), null,
      goog.positioning.Overflow.RESIZE_HEIGHT, null, viewport.toBox());
  assertEquals(
      'Status should be HEIGHT_ADJUSTED.',
      goog.positioning.OverflowStatus.HEIGHT_ADJUSTED, status);

  var TOLERANCE = 0.1;
  // Adjust the viewport to allow some tolerance for subpixel positioning,
  // this is required for this test to pass on IE10,11
  viewport.top -= TOLERANCE;
  viewport.left -= TOLERANCE;

  assertTrue(
      'Popup ' + goog.style.getBounds(popup) + ' not is within viewport' +
          viewport,
      viewport.contains(goog.style.getBounds(popup)));
}


function testPositionAtCoordinateResizeHeight() {
  var f = goog.positioning.positionAtCoordinate;
  var viewport = new goog.math.Box(0, 50, 50, 0);
  var overflow = goog.positioning.Overflow.RESIZE_HEIGHT |
      goog.positioning.Overflow.ADJUST_Y;
  var popup = document.getElementById('popup1');
  var corner = goog.positioning.Corner.BOTTOM_LEFT;

  var pos = newCoord(100, 100);

  assertEquals(
      'Viewport height should be resized.',
      goog.positioning.OverflowStatus.HEIGHT_ADJUSTED |
          goog.positioning.OverflowStatus.ADJUSTED_Y,
      f(pos, popup, corner, undefined, viewport, overflow));
  var bounds = goog.style.getSize(popup);
  assertEquals(
      'Height should be resized to the size of the viewport.', 50,
      bounds.height);
}


function testGetPositionAtCoordinateResizeHeight() {
  var f = goog.positioning.getPositionAtCoordinate;
  var viewport = new goog.math.Box(0, 50, 50, 0);
  var overflow = goog.positioning.Overflow.RESIZE_HEIGHT |
      goog.positioning.Overflow.ADJUST_Y;
  var popup = document.getElementById('popup1');
  var corner = goog.positioning.Corner.BOTTOM_LEFT;

  var pos = newCoord(100, 100);
  var size = goog.style.getSize(popup);

  var result = f(pos, size, corner, undefined, viewport, overflow);
  assertEquals(
      'Viewport height should be resized.',
      goog.positioning.OverflowStatus.HEIGHT_ADJUSTED |
          goog.positioning.OverflowStatus.ADJUSTED_Y,
      result.status);
  assertEquals(
      'Height should be resized to the size of the viewport.', 50,
      result.rect.height);
}


function testGetEffectiveCornerLeftToRight() {
  var f = goog.positioning.getEffectiveCorner;
  var el = document.getElementById('ltr');

  assertEquals(
      'TOP_LEFT should be unchanged for ltr.', corner.TOP_LEFT,
      f(el, corner.TOP_LEFT));
  assertEquals(
      'TOP_RIGHT should be unchanged for ltr.', corner.TOP_RIGHT,
      f(el, corner.TOP_RIGHT));
  assertEquals(
      'BOTTOM_LEFT should be unchanged for ltr.', corner.BOTTOM_LEFT,
      f(el, corner.BOTTOM_LEFT));
  assertEquals(
      'BOTTOM_RIGHT should be unchanged for ltr.', corner.BOTTOM_RIGHT,
      f(el, corner.BOTTOM_RIGHT));

  assertEquals(
      'TOP_START should be TOP_LEFT for ltr.', corner.TOP_LEFT,
      f(el, corner.TOP_START));
  assertEquals(
      'TOP_END should be TOP_RIGHT for ltr.', corner.TOP_RIGHT,
      f(el, corner.TOP_END));
  assertEquals(
      'BOTTOM_START should be BOTTOM_LEFT for ltr.', corner.BOTTOM_LEFT,
      f(el, corner.BOTTOM_START));
  assertEquals(
      'BOTTOM_END should be BOTTOM_RIGHT for ltr.', corner.BOTTOM_RIGHT,
      f(el, corner.BOTTOM_END));
}


function testGetEffectiveCornerRightToLeft() {
  var f = goog.positioning.getEffectiveCorner;
  var el = document.getElementById('rtl');

  assertEquals(
      'TOP_LEFT should be unchanged for rtl.', corner.TOP_LEFT,
      f(el, corner.TOP_LEFT));
  assertEquals(
      'TOP_RIGHT should be unchanged for rtl.', corner.TOP_RIGHT,
      f(el, corner.TOP_RIGHT));
  assertEquals(
      'BOTTOM_LEFT should be unchanged for rtl.', corner.BOTTOM_LEFT,
      f(el, corner.BOTTOM_LEFT));
  assertEquals(
      'BOTTOM_RIGHT should be unchanged for rtl.', corner.BOTTOM_RIGHT,
      f(el, corner.BOTTOM_RIGHT));

  assertEquals(
      'TOP_START should be TOP_RIGHT for rtl.', corner.TOP_RIGHT,
      f(el, corner.TOP_START));
  assertEquals(
      'TOP_END should be TOP_LEFT for rtl.', corner.TOP_LEFT,
      f(el, corner.TOP_END));
  assertEquals(
      'BOTTOM_START should be BOTTOM_RIGHT for rtl.', corner.BOTTOM_RIGHT,
      f(el, corner.BOTTOM_START));
  assertEquals(
      'BOTTOM_END should be BOTTOM_LEFT for rtl.', corner.BOTTOM_LEFT,
      f(el, corner.BOTTOM_END));
}


function testFlipCornerHorizontal() {
  var f = goog.positioning.flipCornerHorizontal;

  assertEquals(
      'TOP_LEFT should be flipped to TOP_RIGHT.', corner.TOP_RIGHT,
      f(corner.TOP_LEFT));
  assertEquals(
      'TOP_RIGHT should be flipped to TOP_LEFT.', corner.TOP_LEFT,
      f(corner.TOP_RIGHT));
  assertEquals(
      'BOTTOM_LEFT should be flipped to BOTTOM_RIGHT.', corner.BOTTOM_RIGHT,
      f(corner.BOTTOM_LEFT));
  assertEquals(
      'BOTTOM_RIGHT should be flipped to BOTTOM_LEFT.', corner.BOTTOM_LEFT,
      f(corner.BOTTOM_RIGHT));

  assertEquals(
      'TOP_START should be flipped to TOP_END.', corner.TOP_END,
      f(corner.TOP_START));
  assertEquals(
      'TOP_END should be flipped to TOP_START.', corner.TOP_START,
      f(corner.TOP_END));
  assertEquals(
      'BOTTOM_START should be flipped to BOTTOM_END.', corner.BOTTOM_END,
      f(corner.BOTTOM_START));
  assertEquals(
      'BOTTOM_END should be flipped to BOTTOM_START.', corner.BOTTOM_START,
      f(corner.BOTTOM_END));
}


function testFlipCornerVertical() {
  var f = goog.positioning.flipCornerVertical;

  assertEquals(
      'TOP_LEFT should be flipped to BOTTOM_LEFT.', corner.BOTTOM_LEFT,
      f(corner.TOP_LEFT));
  assertEquals(
      'TOP_RIGHT should be flipped to BOTTOM_RIGHT.', corner.BOTTOM_RIGHT,
      f(corner.TOP_RIGHT));
  assertEquals(
      'BOTTOM_LEFT should be flipped to TOP_LEFT.', corner.TOP_LEFT,
      f(corner.BOTTOM_LEFT));
  assertEquals(
      'BOTTOM_RIGHT should be flipped to TOP_RIGHT.', corner.TOP_RIGHT,
      f(corner.BOTTOM_RIGHT));

  assertEquals(
      'TOP_START should be flipped to BOTTOM_START.', corner.BOTTOM_START,
      f(corner.TOP_START));
  assertEquals(
      'TOP_END should be flipped to BOTTOM_END.', corner.BOTTOM_END,
      f(corner.TOP_END));
  assertEquals(
      'BOTTOM_START should be flipped to TOP_START.', corner.TOP_START,
      f(corner.BOTTOM_START));
  assertEquals(
      'BOTTOM_END should be flipped to TOP_END.', corner.TOP_END,
      f(corner.BOTTOM_END));
}


function testFlipCorner() {
  var f = goog.positioning.flipCorner;

  assertEquals(
      'TOP_LEFT should be flipped to BOTTOM_RIGHT.', corner.BOTTOM_RIGHT,
      f(corner.TOP_LEFT));
  assertEquals(
      'TOP_RIGHT should be flipped to BOTTOM_LEFT.', corner.BOTTOM_LEFT,
      f(corner.TOP_RIGHT));
  assertEquals(
      'BOTTOM_LEFT should be flipped to TOP_RIGHT.', corner.TOP_RIGHT,
      f(corner.BOTTOM_LEFT));
  assertEquals(
      'BOTTOM_RIGHT should be flipped to TOP_LEFT.', corner.TOP_LEFT,
      f(corner.BOTTOM_RIGHT));

  assertEquals(
      'TOP_START should be flipped to BOTTOM_END.', corner.BOTTOM_END,
      f(corner.TOP_START));
  assertEquals(
      'TOP_END should be flipped to BOTTOM_START.', corner.BOTTOM_START,
      f(corner.TOP_END));
  assertEquals(
      'BOTTOM_START should be flipped to TOP_END.', corner.TOP_END,
      f(corner.BOTTOM_START));
  assertEquals(
      'BOTTOM_END should be flipped to TOP_START.', corner.TOP_START,
      f(corner.BOTTOM_END));
}

function testPositionAtAnchorFrameViewportStandard() {
  var iframe = document.getElementById('iframe-standard');
  var iframeDoc = goog.dom.getFrameContentDocument(iframe);
  assertTrue(new goog.dom.DomHelper(iframeDoc).isCss1CompatMode());

  new goog.dom.DomHelper(iframeDoc).getDocumentScrollElement().scrollTop = 100;
  var anchor = iframeDoc.getElementById('anchor1');
  var popup = document.getElementById('popup6');

  var status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_RIGHT, popup, corner.BOTTOM_RIGHT);
  var iframeRect = goog.style.getBounds(iframe);
  var popupRect = goog.style.getBounds(popup);
  assertEquals(
      'Status should not have any ADJUSTED and FAILED.',
      goog.positioning.OverflowStatus.NONE, status);
  assertRoundedEquals(
      'Popup should be positioned just above the iframe, ' +
          'not above the anchor element inside the iframe',
      iframeRect.top, popupRect.top + popupRect.height);
}

function testPositionAtAnchorFrameViewportQuirk() {
  var iframe = document.getElementById('iframe-quirk');
  var iframeDoc = goog.dom.getFrameContentDocument(iframe);
  assertFalse(new goog.dom.DomHelper(iframeDoc).isCss1CompatMode());

  window.scrollTo(0, 100);
  new goog.dom.DomHelper(iframeDoc).getDocumentScrollElement().scrollTop = 100;
  var anchor = iframeDoc.getElementById('anchor1');
  var popup = document.getElementById('popup6');

  var status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_RIGHT, popup, corner.BOTTOM_RIGHT);
  var iframeRect = goog.style.getBounds(iframe);
  var popupRect = goog.style.getBounds(popup);
  assertEquals(
      'Status should not have any ADJUSTED and FAILED.',
      goog.positioning.OverflowStatus.NONE, status);
  assertRoundedEquals(
      'Popup should be positioned just above the iframe, ' +
          'not above the anchor element inside the iframe',
      iframeRect.top, popupRect.top + popupRect.height);
}

function testPositionAtAnchorFrameViewportWithPopupInScroller() {
  var iframe = document.getElementById('iframe-standard');
  var iframeDoc = goog.dom.getFrameContentDocument(iframe);

  new goog.dom.DomHelper(iframeDoc).getDocumentScrollElement().scrollTop = 100;
  var anchor = iframeDoc.getElementById('anchor1');
  var popup = document.getElementById('popup7');
  popup.offsetParent.scrollTop = 50;

  var status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_RIGHT, popup, corner.BOTTOM_RIGHT);
  var iframeRect = goog.style.getBounds(iframe);
  var popupRect = goog.style.getBounds(popup);
  assertEquals(
      'Status should not have any ADJUSTED and FAILED.',
      goog.positioning.OverflowStatus.NONE, status);
  assertRoughlyEquals(
      'Popup should be positioned just above the iframe, ' +
          'not above the anchor element inside the iframe',
      iframeRect.top, popupRect.top + popupRect.height, ALLOWED_OFFSET);
}

function testPositionAtAnchorNestedFrames() {
  var outerIframe = document.getElementById('nested-outer');
  var outerDoc = goog.dom.getFrameContentDocument(outerIframe);
  var popup = outerDoc.getElementById('popup1');
  var innerIframe = outerDoc.getElementById('inner-frame');
  var innerDoc = goog.dom.getFrameContentDocument(innerIframe);
  var anchor = innerDoc.getElementById('anchor1');

  var status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.BOTTOM_LEFT);
  assertEquals(
      'Status should not have any ADJUSTED and FAILED.',
      goog.positioning.OverflowStatus.NONE, status);
  var innerIframeRect = goog.style.getBounds(innerIframe);
  var popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Top of frame should align with bottom of the popup', innerIframeRect.top,
      popupRect.top + popupRect.height);

  // The anchor is scrolled up by 10px.
  // Popup position should be the same as above.
  goog.dom.getWindow(innerDoc).scrollTo(0, 10);
  status = goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.BOTTOM_LEFT);
  assertEquals(
      'Status should not have any ADJUSTED and FAILED.',
      goog.positioning.OverflowStatus.NONE, status);
  innerIframeRect = goog.style.getBounds(innerIframe);
  popupRect = goog.style.getBounds(popup);
  assertRoundedEquals(
      'Top of frame should align with bottom of the popup', innerIframeRect.top,
      popupRect.top + popupRect.height);
}

function testPositionAtAnchorOffscreen() {
  var offset = 0;
  var anchor = goog.dom.getElement('offscreen-anchor');
  var popup = goog.dom.getElement('popup3');

  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectEquals(newCoord(offset, offset), goog.style.getPageOffset(popup));

  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, null, null,
      overflow.ADJUST_X_EXCEPT_OFFSCREEN | overflow.ADJUST_Y);
  assertObjectEquals(newCoord(-1000, offset), goog.style.getPageOffset(popup));

  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y_EXCEPT_OFFSCREEN);
  assertObjectEquals(newCoord(offset, -1000), goog.style.getPageOffset(popup));

  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_LEFT, null, null,
      overflow.ADJUST_X_EXCEPT_OFFSCREEN | overflow.ADJUST_Y_EXCEPT_OFFSCREEN);
  assertObjectEquals(newCoord(-1000, -1000), goog.style.getPageOffset(popup));
}

function testPositionAtAnchorWithOverflowScrollOffsetParent() {
  var testAreaOffset = goog.style.getPageOffset(testArea);
  var scrollbarWidth = goog.style.getScrollbarWidth();
  window.scrollTo(testAreaOffset.x, testAreaOffset.y);

  var overflowDiv = goog.dom.createElement(goog.dom.TagName.DIV);
  overflowDiv.style.overflow = 'scroll';
  overflowDiv.style.position = 'relative';
  goog.style.setSize(overflowDiv, 200 /* width */, 100 /* height */);

  var anchor = goog.dom.createElement(goog.dom.TagName.DIV);
  anchor.style.position = 'absolute';
  goog.style.setSize(anchor, 50 /* width */, 50 /* height */);
  goog.style.setPosition(anchor, 300 /* left */, 300 /* top */);

  var popup = createPopupDiv(75 /* width */, 50 /* height */);

  goog.dom.append(testArea, overflowDiv, anchor);
  goog.dom.append(overflowDiv, popup);

  // Popup should always be positioned within the overflowDiv
  goog.style.setPosition(overflowDiv, 0 /* left */, 0 /* top */);
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectRoughlyEquals(
      new goog.math.Coordinate(
          testAreaOffset.x + 200 - 75 - scrollbarWidth,
          testAreaOffset.y + 100 - 50 - scrollbarWidth),
      goog.style.getPageOffset(popup), 1);

  goog.style.setPosition(overflowDiv, 400 /* left */, 0 /* top */);
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_RIGHT, popup, corner.TOP_LEFT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectRoughlyEquals(
      new goog.math.Coordinate(
          testAreaOffset.x + 400, testAreaOffset.y + 100 - 50 - scrollbarWidth),
      goog.style.getPageOffset(popup), 1);

  goog.style.setPosition(overflowDiv, 0 /* left */, 400 /* top */);
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_LEFT, popup, corner.BOTTOM_RIGHT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectRoughlyEquals(
      new goog.math.Coordinate(
          testAreaOffset.x + 200 - 75 - scrollbarWidth, testAreaOffset.y + 400),
      goog.style.getPageOffset(popup), 1);

  goog.style.setPosition(overflowDiv, 400 /* left */, 400 /* top */);
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_RIGHT, popup, corner.BOTTOM_LEFT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectRoughlyEquals(
      new goog.math.Coordinate(testAreaOffset.x + 400, testAreaOffset.y + 400),
      goog.style.getPageOffset(popup), 1);

  // No overflow.
  goog.style.setPosition(overflowDiv, 300 - 50 /* left */, 300 /* top */);
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectRoughlyEquals(
      new goog.math.Coordinate(
          testAreaOffset.x + 300 - 50, testAreaOffset.y + 300),
      goog.style.getPageOffset(popup), 1);
}

function testPositionAtAnchorWithOverflowHiddenParent() {
  var testAreaOffset = goog.style.getPageOffset(testArea);
  window.scrollTo(testAreaOffset.x, testAreaOffset.y);

  var overflowDiv = goog.dom.createElement(goog.dom.TagName.DIV);
  overflowDiv.style.overflow = 'hidden';
  overflowDiv.style.position = 'relative';
  goog.style.setSize(overflowDiv, 200 /* width */, 100 /* height */);

  var anchor = goog.dom.createElement(goog.dom.TagName.DIV);
  anchor.style.position = 'absolute';
  goog.style.setSize(anchor, 50 /* width */, 50 /* height */);
  goog.style.setPosition(anchor, 300 /* left */, 300 /* top */);

  var popup = createPopupDiv(75 /* width */, 50 /* height */);

  goog.dom.append(testArea, overflowDiv, anchor);
  goog.dom.append(overflowDiv, popup);

  // Popup should always be positioned within the overflowDiv
  goog.style.setPosition(overflowDiv, 0 /* left */, 0 /* top */);
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectRoughlyEquals(
      new goog.math.Coordinate(
          testAreaOffset.x + 200 - 75, testAreaOffset.y + 100 - 50),
      goog.style.getPageOffset(popup), 1);

  goog.style.setPosition(overflowDiv, 400 /* left */, 0 /* top */);
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_RIGHT, popup, corner.TOP_LEFT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectRoughlyEquals(
      new goog.math.Coordinate(
          testAreaOffset.x + 400, testAreaOffset.y + 100 - 50),
      goog.style.getPageOffset(popup), 1);

  goog.style.setPosition(overflowDiv, 0 /* left */, 400 /* top */);
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_LEFT, popup, corner.BOTTOM_RIGHT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectRoughlyEquals(
      new goog.math.Coordinate(
          testAreaOffset.x + 200 - 75, testAreaOffset.y + 400),
      goog.style.getPageOffset(popup), 1);

  goog.style.setPosition(overflowDiv, 400 /* left */, 400 /* top */);
  goog.positioning.positionAtAnchor(
      anchor, corner.BOTTOM_RIGHT, popup, corner.BOTTOM_LEFT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectRoughlyEquals(
      new goog.math.Coordinate(testAreaOffset.x + 400, testAreaOffset.y + 400),
      goog.style.getPageOffset(popup), 1);

  // No overflow.
  goog.style.setPosition(overflowDiv, 300 - 50 /* left */, 300 /* top */);
  goog.positioning.positionAtAnchor(
      anchor, corner.TOP_LEFT, popup, corner.TOP_RIGHT, null, null,
      overflow.ADJUST_X | overflow.ADJUST_Y);
  assertObjectRoughlyEquals(
      new goog.math.Coordinate(
          testAreaOffset.x + 300 - 50, testAreaOffset.y + 300),
      goog.style.getPageOffset(popup), 1);
}

function createPopupDiv(width, height) {
  var popupDiv = goog.dom.createElement(goog.dom.TagName.DIV);
  popupDiv.style.position = 'absolute';
  goog.style.setSize(popupDiv, width, height);
  goog.style.setPosition(popupDiv, 0 /* left */, 250 /* top */);
  return popupDiv;
}

function newCoord(x, y) {
  return new goog.math.Coordinate(x, y);
}

function newSize(w, h) {
  return new goog.math.Size(w, h);
}

function newBox(coord, size) {}