chromium/third_party/google-closure-library/closure/goog/ui/ac/renderer_test.js

/**
 * @license
 * Copyright The Closure Library Authors.
 * SPDX-License-Identifier: Apache-2.0
 */

goog.module('goog.ui.ac.RendererTest');
goog.setTestOnly();

const AutoComplete = goog.require('goog.ui.ac.AutoComplete');
const FadeInAndShow = goog.require('goog.fx.dom.FadeInAndShow');
const FadeOutAndHide = goog.require('goog.fx.dom.FadeOutAndHide');
const PropertyReplacer = goog.require('goog.testing.PropertyReplacer');
const Renderer = goog.require('goog.ui.ac.Renderer');
const State = goog.require('goog.a11y.aria.State');
const TagName = goog.require('goog.dom.TagName');
const aria = goog.require('goog.a11y.aria');
const classlist = goog.require('goog.dom.classlist');
const dispose = goog.require('goog.dispose');
const dom = goog.require('goog.dom');
const events = goog.require('goog.events');
const googString = goog.require('goog.string');
const style = goog.require('goog.style');
const testSuite = goog.require('goog.testing.testSuite');

let renderer;
const rendRows = [];
let someElement;
let target;
let viewport;
let viewportTarget;
let widthProvider;
let maxWidthProvider;
let propertyReplacer;

// One-time set up of rows formatted for the renderer.
const rows = [
  'Amanda Annie Anderson',
  'Frankie Manning',
  'Louis D Armstrong',
  // NOTE(user): sorry about this test input, but it has caused problems
  // in the past, so I want to make sure to test against it.
  'Foo Bar................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................................',
  '<div><div>test</div></div>',
  '<div><div>test1</div><div>test2</div></div>',
  '<div>random test string<div>test1</div><div><div>test2</div><div>test3</div></div></div>',
];

for (let i = 0; i < rows.length; i++) {
  rendRows.push({id: i, data: rows[i]});
}

// ------- Helper functions -------

// The default rowRenderer will escape any HTML in the row content.
// Activating HTML rendering will allow HTML strings to be rendered to DOM
// instead of being escaped.
function enableHtmlRendering(renderer) {
  let customRendererInternal = {
    renderRow: function(row, token, node) {
      node.innerHTML = row.data.toString();
    },
  };
}

function assertNumBoldTags(boldTagElArray, expectedNum) {
  assertEquals(
      'Incorrect number of bold tags', expectedNum, boldTagElArray.length);
}

function assertPreviousNodeText(boldTag, expectedText) {
  const prevNode = boldTag.previousSibling;
  assertEquals(
      'Expected text before the token does not match', expectedText,
      prevNode.nodeValue);
}

function assertHighlightedText(boldTag, expectedHighlightedText) {
  assertEquals(
      'Incorrect text bolded', expectedHighlightedText, boldTag.innerHTML);
}

function assertLastNodeText(node, expectedText) {
  const lastNode = node.lastChild;
  assertEquals(
      'Incorrect text in the last node', expectedText, lastNode.nodeValue);
}
testSuite({
  setUpPage() {
    someElement = dom.getElement('someElement');
    target = dom.getElement('target');
    viewport = dom.getElement('viewport');
    viewportTarget = dom.getElement('viewportTarget');
    widthProvider = dom.getElement('widthProvider');
    maxWidthProvider = dom.getElement('maxWidthProvider');
    propertyReplacer = new PropertyReplacer();
  },

  setUp() {
    renderer = new Renderer();
    /** @suppress {visibility} suppression added to enable type checking */
    renderer.rowDivs_ = [];
    /** @suppress {visibility} suppression added to enable type checking */
    renderer.target_ = target;
  },

  tearDown() {
    renderer.dispose();
    propertyReplacer.reset();
  },

  testBasicMatchingWithHtmlRow() {
    // '<div><div>test</div></div>'
    const row = rendRows[4];
    const token = 'te';
    enableHtmlRendering(renderer);
    const node = renderer.renderRowHtml(row, token);
    const boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
  },

  testShouldMatchOnlyOncePerDefaultWithComplexHtmlStrings() {
    // '<div><div>test1</div><div>test2</div></div>'
    const row = rendRows[5];
    const token = 'te';
    enableHtmlRendering(renderer);
    const node = renderer.renderRowHtml(row, token);
    const boldTagElArray = dom.getElementsByTagName(TagName.B, node);

    // It should match and render highlighting for the first 'test1' and
    // stop here. This is the default behavior of the renderer.
    assertNumBoldTags(boldTagElArray, 1);
  },

  testShouldMatchMultipleTimesWithComplexHtmlStrings() {
    renderer.setHighlightAllTokens(true);

    // '<div><div>test1</div><div>test2</div></div>'
    let row = rendRows[5];
    const token = 'te';
    enableHtmlRendering(renderer);
    let node = renderer.renderRowHtml(row, token);
    let boldTagElArray = dom.getElementsByTagName(TagName.B, node);

    // It should match and render highlighting for both 'test1' and 'test2'.
    assertNumBoldTags(boldTagElArray, 2);

    // Try again with a more complex HTML string.
    // '<div>random test
    // string<div>test1</div><div><div>test2</div><div>test3</div></div></div>'
    row = rendRows[6];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    // It should match 'test', 'test1', 'test2' and 'test3' wherever
    // they are in the DOM tree.
    assertNumBoldTags(boldTagElArray, 4);
  },

  testBasicStringTokenHighlightingUsingUniversalMatching() {
    const row = rendRows[0];  // 'Amanda Annie Anderson'
    renderer.setMatchWordBoundary(false);

    // Should highlight first match only.
    let token = 'A';
    let node = renderer.renderRowHtml(row, token);
    let boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], '');
    assertHighlightedText(boldTagElArray[0], 'A');
    assertLastNodeText(node, 'manda Annie Anderson');

    // Match should be case insensitive, and should match tokens in the
    // middle of words if useWordMatching is turned off ("an" in Amanda).
    token = 'an';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Am');
    assertHighlightedText(boldTagElArray[0], 'an');
    assertLastNodeText(node, 'da Annie Anderson');

    // Should only match on non-empty strings.
    token = '';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Amanda Annie Anderson');

    // Should not match leading whitespace.
    token = ' an';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Amanda Annie Anderson');
  },

  testBasicStringTokenHighlighting() {
    let row = rendRows[0];  // 'Amanda Annie Anderson'

    // Should highlight first match only.
    let token = 'A';
    let node = renderer.renderRowHtml(row, token);
    let boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], '');
    assertHighlightedText(boldTagElArray[0], 'A');
    assertLastNodeText(node, 'manda Annie Anderson');

    // Should only match on non-empty strings.
    token = '';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Amanda Annie Anderson');

    // Match should be case insensitive, and should not match tokens in the
    // middle of words ("an" in Amanda).
    token = 'an';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Amanda ');
    assertHighlightedText(boldTagElArray[0], 'An');
    assertLastNodeText(node, 'nie Anderson');

    // Should not match whitespace.
    token = ' ';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Amanda Annie Anderson');

    // Should not match leading whitespace since all matches are at the start of
    // word boundaries.
    token = ' an';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Amanda Annie Anderson');

    // Should match trailing whitespace.
    token = 'annie ';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Amanda ');
    assertHighlightedText(boldTagElArray[0], 'Annie ');
    assertLastNodeText(node, 'Anderson');

    // Should match across whitespace.
    row = rendRows[2];  // 'Louis D Armstrong'
    token = 'd a';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Louis ');
    assertHighlightedText(boldTagElArray[0], 'D A');
    assertLastNodeText(node, 'rmstrong');

    // Should match the last token.
    token = 'aRmStRoNg';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Louis D ');
    assertHighlightedText(boldTagElArray[0], 'Armstrong');
    assertLastNodeText(node, '');
  },

  // The name of this function is fortuitous, in that it gets tested
  // last on FF. The lazy regexp on FF is particularly slow, and causes
  // the test to take a long time, and sometimes time out when run on forge
  // because it triggers the test runner to go back to the event loop...
  testPathologicalInput() {
    // Should not hang on bizarrely long strings
    const row = rendRows[3];  // pathological row
    const token = 'foo';
    const node = renderer.renderRowHtml(row, token);
    const boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertHighlightedText(boldTagElArray[0], 'Foo');
    assert(googString.startsWith(
        boldTagElArray[0].nextSibling.nodeValue, ' Bar...'));
  },

  testBasicArrayTokenHighlighting() {
    let row = rendRows[1];  // 'Frankie Manning'

    // Only the first match in the array should be highlighted.
    let token = ['f', 'm'];
    let node = renderer.renderRowHtml(row, token);
    let boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], '');
    assertHighlightedText(boldTagElArray[0], 'F');
    assertLastNodeText(node, 'rankie Manning');

    // Only the first match in the array should be highlighted.
    token = ['m', 'f'];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Frankie ');
    assertHighlightedText(boldTagElArray[0], 'M');
    assertLastNodeText(node, 'anning');

    // Skip tokens that do not match.
    token = ['asdf', 'f'];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], '');
    assertHighlightedText(boldTagElArray[0], 'F');
    assertLastNodeText(node, 'rankie Manning');

    // Highlight nothing if no tokens match.
    token = ['Foo', 'bar', 'baz'];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Frankie Manning');

    // Empty array should not match.
    token = [];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Frankie Manning');

    // Empty string in array should not match.
    token = [''];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Frankie Manning');

    // Whitespace in array should not match.
    token = [' '];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Frankie Manning');

    // Whitespace entries in array should not match.
    token = [' ', 'man'];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Frankie ');
    assertHighlightedText(boldTagElArray[0], 'Man');
    assertLastNodeText(node, 'ning');

    // Whitespace in array entry should match as a whole token.
    row = rendRows[2];  // 'Louis D Armstrong'
    token = ['d arm', 'lou'];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Louis ');
    assertHighlightedText(boldTagElArray[0], 'D Arm');
    assertLastNodeText(node, 'strong');
  },

  testHighlightAllTokensSingleTokenHighlighting() {
    renderer.setHighlightAllTokens(true);
    const row = rendRows[0];  // 'Amanda Annie Anderson'

    // All matches at the start of the word should be highlighted when
    // highlightAllTokens is set.
    let token = 'a';
    let node = renderer.renderRowHtml(row, token);
    let boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 3);
    assertPreviousNodeText(boldTagElArray[0], '');
    assertHighlightedText(boldTagElArray[0], 'A');
    assertPreviousNodeText(boldTagElArray[1], 'manda ');
    assertHighlightedText(boldTagElArray[1], 'A');
    assertPreviousNodeText(boldTagElArray[2], 'nnie ');
    assertHighlightedText(boldTagElArray[2], 'A');
    assertLastNodeText(node, 'nderson');

    // Should not match on empty string.
    token = '';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Amanda Annie Anderson');

    // Match should be case insensitive.
    token = 'AN';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 2);
    assertPreviousNodeText(boldTagElArray[0], 'Amanda ');
    assertHighlightedText(boldTagElArray[0], 'An');
    assertPreviousNodeText(boldTagElArray[1], 'nie ');
    assertHighlightedText(boldTagElArray[1], 'An');
    assertLastNodeText(node, 'derson');

    // Should not match on whitespace.
    token = ' ';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Amanda Annie Anderson');

    // When highlighting all tokens, should match despite leading whitespace.
    token = '  am';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], '');
    assertHighlightedText(boldTagElArray[0], 'Am');
    assertLastNodeText(node, 'anda Annie Anderson');

    // Should match with trailing whitepsace.
    token = 'ann   ';
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Amanda ');
    assertHighlightedText(boldTagElArray[0], 'Ann');
    assertLastNodeText(node, 'ie Anderson');
  },

  testHighlightAllTokensMultipleStringTokenHighlighting() {
    renderer.setHighlightAllTokens(true);
    const row = rendRows[1];  // 'Frankie Manning'

    // Each individual space-separated token should match.
    const token = 'm F';
    const node = renderer.renderRowHtml(row, token);
    const boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 2);
    assertPreviousNodeText(boldTagElArray[0], '');
    assertHighlightedText(boldTagElArray[0], 'F');
    assertPreviousNodeText(boldTagElArray[1], 'rankie ');
    assertHighlightedText(boldTagElArray[1], 'M');
    assertLastNodeText(node, 'anning');
  },

  testHighlightAllTokensArrayTokenHighlighting() {
    renderer.setHighlightAllTokens(true);
    const row = rendRows[0];  // 'Amanda Annie Anderson'

    // All tokens in the array should match.
    let token = ['AM', 'AN'];
    let node = renderer.renderRowHtml(row, token);
    let boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 3);
    assertPreviousNodeText(boldTagElArray[0], '');
    assertHighlightedText(boldTagElArray[0], 'Am');
    assertPreviousNodeText(boldTagElArray[1], 'anda ');
    assertHighlightedText(boldTagElArray[1], 'An');
    assertPreviousNodeText(boldTagElArray[2], 'nie ');
    assertHighlightedText(boldTagElArray[2], 'An');
    assertLastNodeText(node, 'derson');

    // Empty array should not match.
    token = [];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Amanda Annie Anderson');

    // Empty string in array should not match.
    token = [''];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Amanda Annie Anderson');

    // Whitespace in array should not match.
    token = [' '];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 0);
    assertLastNodeText(node, 'Amanda Annie Anderson');

    // Empty string entries in array should not match.
    token = ['', 'Ann'];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Amanda ');
    assertHighlightedText(boldTagElArray[0], 'Ann');
    assertLastNodeText(node, 'ie Anderson');

    // Whitespace entries in array should not match.
    token = [' ', 'And'];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 1);
    assertPreviousNodeText(boldTagElArray[0], 'Amanda Annie ');
    assertHighlightedText(boldTagElArray[0], 'And');
    assertLastNodeText(node, 'erson');

    // Whitespace in array entry should match as a whole token.
    token = ['annie a', 'Am'];
    node = renderer.renderRowHtml(row, token);
    boldTagElArray = dom.getElementsByTagName(TagName.B, node);
    assertNumBoldTags(boldTagElArray, 2);
    assertPreviousNodeText(boldTagElArray[0], '');
    assertHighlightedText(boldTagElArray[0], 'Am');
    assertPreviousNodeText(boldTagElArray[1], 'anda ');
    assertHighlightedText(boldTagElArray[1], 'Annie A');
    assertLastNodeText(node, 'nderson');
  },

  /** @suppress {visibility} suppression added to enable type checking */
  testMenuFadeDuration() {
    renderer.maybeCreateElement_();

    let hideCalled = false;
    let hideAnimCalled = false;
    let showCalled = false;
    let showAnimCalled = false;

    propertyReplacer.set(style, 'setElementShown', (el, state) => {
      if (state) {
        showCalled = true;
      } else {
        hideCalled = true;
      }
    });

    propertyReplacer.set(FadeInAndShow.prototype, 'play', () => {
      showAnimCalled = true;
    });

    propertyReplacer.set(FadeOutAndHide.prototype, 'play', () => {
      hideAnimCalled = true;
    });

    // Default behavior does show/hide but not animations.

    renderer.show();
    assertTrue(showCalled);
    assertFalse(showAnimCalled);

    renderer.dismiss();
    assertTrue(hideCalled);
    assertFalse(hideAnimCalled);

    // But animations can be turned on.

    showCalled = false;
    hideCalled = false;
    renderer.setMenuFadeDuration(100);

    renderer.show();
    assertFalse(showCalled);
    assertTrue(showAnimCalled);

    renderer.dismiss();
    assertFalse(hideCalled);
    assertTrue(hideAnimCalled);
  },

  /**
     @suppress {visibility,checkTypes} suppression added to enable type
     checking
   */
  testAriaTags() {
    renderer.maybeCreateElement_();

    assertNotNull(target);
    assertEvaluatesToFalse('The role should be empty.', aria.getRole(target));
    assertEquals('', aria.getState(target, State.HASPOPUP));
    assertEquals('', aria.getState(renderer.getElement(), State.EXPANDED));
    assertEquals('', aria.getState(target, State.OWNS));

    renderer.show();
    assertEquals('true', aria.getState(target, State.HASPOPUP));
    assertEquals('true', aria.getState(target, State.EXPANDED));
    assertEquals('true', aria.getState(renderer.getElement(), State.EXPANDED));
    assertEquals(renderer.getElement().id, aria.getState(target, State.OWNS));

    renderer.dismiss();
    assertEquals('false', aria.getState(target, State.HASPOPUP));
    assertEquals('false', aria.getState(target, State.EXPANDED));
    assertEquals('false', aria.getState(renderer.getElement(), State.EXPANDED));
    assertEquals('', aria.getState(target, State.OWNS));
  },

  /** @suppress {visibility} suppression added to enable type checking */
  testHiliteRowWithDefaultRenderer() {
    renderer.renderRows(rendRows, '');
    renderer.hiliteRow(2);
    assertEquals(2, renderer.hilitedRow_);
    assertTrue(
        classlist.contains(renderer.rowDivs_[2], renderer.activeClassName));
  },

  /** @suppress {visibility} suppression added to enable type checking */
  testHiliteRowWithCustomRenderer() {
    dispose(renderer);

    // Use a custom renderer that doesn't put the result divs as direct children
    // of this.element_.
    const customRenderer = {
      render: function(renderer, element, rows, token) {
        // Put all of the results into a results holder div that is a child of
        // this.element_.
        const resultsHolder = dom.createDom(TagName.DIV);
        dom.appendChild(element, resultsHolder);
        let row;
        for (let i = 0; row = rows[i]; ++i) {
          const node = renderer.renderRowHtml(row, token);
          dom.appendChild(resultsHolder, node);
        }
      },
    };
    renderer = new Renderer(null, customRenderer);

    // Make sure we can still highlight the row at position 2 even though
    // this.element_.childNodes contains only a single child.
    renderer.renderRows(rendRows, '');
    renderer.hiliteRow(2);
    assertEquals(2, renderer.hilitedRow_);
    assertTrue(
        classlist.contains(renderer.rowDivs_[2], renderer.activeClassName));
  },

  testReposition() {
    renderer.renderRows(rendRows, '', target);
    const el = renderer.getElement();
    el.style.position = 'absolute';
    el.style.width = '100px';

    renderer.setAutoPosition(true);
    renderer.redraw();

    const rendererOffset = style.getPageOffset(renderer.getElement());
    const rendererSize = style.getSize(renderer.getElement());
    const targetOffset = style.getPageOffset(target);
    const targetSize = style.getSize(target);

    assertEquals(0 + targetOffset.x, rendererOffset.x);
    assertRoughlyEquals(
        targetOffset.y + targetSize.height, rendererOffset.y, 1);
  },

  testSetWidthProvider() {
    renderer.setWidthProvider(widthProvider);
    renderer.renderRows(rendRows, '');
    const el = renderer.getElement();
    // Set a width that's smaller than widthProvider.
    el.style.width = '1px';

    renderer.redraw();

    const rendererSize = style.getSize(el);
    const widthProviderSize = style.getSize(widthProvider);
    assertEquals(rendererSize.width, widthProviderSize.width);
  },

  testSetWidthProviderWithBorderWidth() {
    const borderWidth = 5;
    renderer.setWidthProvider(widthProvider, borderWidth);
    renderer.renderRows(rendRows, '');
    const el = renderer.getElement();
    // Set a width that's smaller than widthProvider.
    el.style.width = '1px';

    renderer.redraw();

    const rendererSize = style.getSize(el);
    const widthProviderSize = style.getSize(widthProvider);
    assertEquals(rendererSize.width, widthProviderSize.width - borderWidth);
  },

  testSetWidthProviderWithBorderWidthAndMaxWidthProvider() {
    const borderWidth = 5;
    renderer.setWidthProvider(widthProvider, borderWidth, maxWidthProvider);
    renderer.renderRows(rendRows, '');
    const el = renderer.getElement();
    // Set a width that's larger than maxWidthProvider.
    el.style.width = '250px';

    renderer.redraw();

    const rendererSize = style.getSize(el);
    const maxWidthProviderSize = style.getSize(maxWidthProvider);
    assertEquals(rendererSize.width, maxWidthProviderSize.width - borderWidth);
  },

  testRepositionWithRightAlign() {
    renderer.renderRows(rendRows, '', target);
    const el = renderer.getElement();
    el.style.position = 'absolute';
    el.style.width = '150px';

    renderer.setAutoPosition(true);
    renderer.setRightAlign(true);
    renderer.redraw();

    const rendererOffset = style.getPageOffset(renderer.getElement());
    const rendererSize = style.getSize(renderer.getElement());
    const targetOffset = style.getPageOffset(target);
    const targetSize = style.getSize(target);

    assertRoughlyEquals(
        targetOffset.x + targetSize.width,
        rendererOffset.x + rendererSize.width, 1);
    assertRoughlyEquals(
        targetOffset.y + targetSize.height, rendererOffset.y, 1);
  },

  testRepositionResizeHeight() {
    renderer = new Renderer(viewport);
    // Render the first 4 rows from test set.
    renderer.renderRows(rendRows.slice(0, 4), '', viewportTarget);
    renderer.setAutoPosition(true);
    renderer.setShowScrollbarsIfTooLarge(true);

    // Stick a huge row in the dropdown element, to make sure it won't
    // fit in the viewport.
    const hugeRow = dom.createDom(TagName.DIV, {style: 'height:1000px'});
    dom.appendChild(renderer.getElement(), hugeRow);

    renderer.reposition();

    let rendererOffset = style.getPageOffset(renderer.getElement());
    let rendererSize = style.getSize(renderer.getElement());
    let viewportOffset = style.getPageOffset(viewport);
    let viewportSize = style.getSize(viewport);

    assertRoughlyEquals(
        viewportOffset.y + viewportSize.height,
        rendererSize.height + rendererOffset.y, 1);

    // Remove the huge row, and make sure that the dropdown element gets shrunk.
    renderer.getElement().removeChild(hugeRow);
    renderer.reposition();

    rendererOffset = style.getPageOffset(renderer.getElement());
    rendererSize = style.getSize(renderer.getElement());
    viewportOffset = style.getPageOffset(viewport);
    viewportSize = style.getSize(viewport);

    assertTrue(
        (rendererSize.height + rendererOffset.y) <
        (viewportOffset.y + viewportSize.height));
  },

  testHiliteEvent() {
    renderer.renderRows(rendRows, '');

    let hiliteEventFired = false;
    events.listenOnce(renderer, AutoComplete.EventType.ROW_HILITE, (e) => {
      hiliteEventFired = true;
      assertEquals(e.row, rendRows[1].data);
    });
    renderer.hiliteRow(1);
    assertTrue(hiliteEventFired);

    hiliteEventFired = false;
    events.listenOnce(renderer, AutoComplete.EventType.ROW_HILITE, (e) => {
      hiliteEventFired = true;
      assertNull(e.row);
    });
    renderer.hiliteRow(rendRows.length);  // i.e. out of bounds.
    assertTrue(hiliteEventFired);
  },
});