Project import generated by Copybara.

GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/static_src/elements/chops/chops-autocomplete/chops-autocomplete.test.js b/static_src/elements/chops/chops-autocomplete/chops-autocomplete.test.js
new file mode 100644
index 0000000..e470312
--- /dev/null
+++ b/static_src/elements/chops/chops-autocomplete/chops-autocomplete.test.js
@@ -0,0 +1,358 @@
+// Copyright 2019 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+import sinon from 'sinon';
+import {assert} from 'chai';
+import {ChopsAutocomplete} from './chops-autocomplete.js';
+
+let element;
+let input;
+
+describe('chops-autocomplete', () => {
+  beforeEach(() => {
+    element = document.createElement('chops-autocomplete');
+    document.body.appendChild(element);
+
+    input = document.createElement('input');
+    input.id = 'autocomplete-input';
+    document.body.appendChild(input);
+
+    element.for = 'autocomplete-input';
+  });
+
+  afterEach(() => {
+    document.body.removeChild(element);
+    document.body.removeChild(input);
+  });
+
+  it('initializes', () => {
+    assert.instanceOf(element, ChopsAutocomplete);
+  });
+
+  it('registers child input', async () => {
+    await element.updateComplete;
+
+    assert.isNotNull(element._forRef);
+    assert.equal(element._forRef.tagName.toUpperCase(), 'INPUT');
+  });
+
+  it('completeValue sets input value', async () => {
+    await element.updateComplete;
+
+    element.completeValue('test');
+    assert.equal(input.value, 'test');
+
+    element.completeValue('again');
+    assert.equal(input.value, 'again');
+  });
+
+  it('completeValue can run a custom replacer', async () => {
+    element.replacer = (input, value) => input.value = value + ',';
+    await element.updateComplete;
+
+    element.completeValue('trailing');
+    assert.equal(input.value, 'trailing,');
+
+    element.completeValue('comma');
+    assert.equal(input.value, 'comma,');
+  });
+
+  it('completions render', async () => {
+    element.completions = ['hello', 'world'];
+    element.docDict = {'hello': 'well hello there'};
+    await element.updateComplete;
+
+    const completions = element.querySelectorAll('.completion');
+    const docstrings = element.querySelectorAll('.docstring');
+
+    assert.equal(completions.length, 2);
+    assert.equal(docstrings.length, 2);
+
+    assert.include(completions[0].textContent, 'hello');
+    assert.include(completions[1].textContent, 'world');
+
+    assert.include(docstrings[0].textContent, 'well hello there');
+    assert.include(docstrings[1].textContent, '');
+  });
+
+  it('completions bold matched section when rendering', async () => {
+    element.completions = ['hello-world'];
+    element._prefix = 'wor';
+    element._matchDict = {
+      'hello-world': {'index': 6},
+    };
+
+    await element.updateComplete;
+
+    const completion = element.querySelector('.completion');
+
+    assert.include(completion.textContent, 'hello-world');
+
+    assert.equal(completion.querySelector('b').textContent.trim(), 'wor');
+  });
+
+
+  it('showCompletions populates completions with matches', async () => {
+    element.strings = [
+      'test-one',
+      'test-two',
+      'ignore',
+      'hello',
+      'woah-test',
+      'i-am-a-tester',
+    ];
+    input.value = 'test';
+    await element.updateComplete;
+
+    element.showCompletions();
+
+    assert.deepEqual(element.completions, [
+      'test-one',
+      'test-two',
+      'woah-test',
+      'i-am-a-tester',
+    ]);
+  });
+
+  it('showCompletions matches docs', async () => {
+    element.strings = [
+      'hello',
+      'world',
+      'no-op',
+    ];
+    element.docDict = {'world': 'this is a test'};
+    input.value = 'test';
+    await element.updateComplete;
+
+    element.showCompletions();
+
+    assert.deepEqual(element.completions, [
+      'world',
+    ]);
+  });
+
+  it('showCompletions caps completions at max', async () => {
+    element.max = 2;
+    element.strings = [
+      'test-one',
+      'test-two',
+      'ignore',
+      'hello',
+      'woah-test',
+      'i-am-a-tester',
+    ];
+    input.value = 'test';
+    await element.updateComplete;
+
+    element.showCompletions();
+
+    assert.deepEqual(element.completions, [
+      'test-one',
+      'test-two',
+    ]);
+  });
+
+  it('hideCompletions hides completions', async () => {
+    element.completions = [
+      'test-one',
+      'test-two',
+    ];
+
+    await element.updateComplete;
+
+    const completionTable = element.querySelector('table');
+    assert.isFalse(completionTable.hidden);
+
+    element.hideCompletions();
+
+    await element.updateComplete;
+
+    assert.isTrue(completionTable.hidden);
+  });
+
+  it('clicking completion completes it', async () => {
+    element.completions = [
+      'test-one',
+      'test-two',
+      'click me!',
+      'test',
+    ];
+
+    await element.updateComplete;
+
+    const completions = element.querySelectorAll('tr');
+
+    assert.equal(input.value, '');
+
+    // Note: the click() event can only trigger click events, not mousedown
+    // events, so we are instead manually running the event handler.
+    element._clickCompletion({
+      preventDefault: sinon.stub(),
+      currentTarget: completions[2],
+    });
+
+    assert.equal(input.value, 'click me!');
+  });
+
+  it('completion is scrolled into view when outside viewport', async () => {
+    element.completions = [
+      'i',
+      'am',
+      'an option',
+    ];
+    element._selectedIndex = 0;
+    element.id = 'chops-autocomplete-1';
+
+    await element.updateComplete;
+
+    const container = element.querySelector('tbody');
+    const completion = container.querySelector('tr');
+    const completionHeight = completion.offsetHeight;
+    // Make the table one row tall.
+    container.style.height = `${completionHeight}px`;
+
+    element._selectedIndex = 1;
+    await element.updateComplete;
+
+    assert.equal(container.scrollTop, completionHeight);
+
+    element._selectedIndex = 2;
+    await element.updateComplete;
+
+    assert.equal(container.scrollTop, completionHeight * 2);
+
+    element._selectedIndex = 0;
+    await element.updateComplete;
+
+    assert.equal(container.scrollTop, 0);
+  });
+
+  it('aria-activedescendant set based on selected option', async () => {
+    element.completions = [
+      'i',
+      'am',
+      'an option',
+    ];
+    element._selectedIndex = 1;
+    element.id = 'chops-autocomplete-1';
+
+    await element.updateComplete;
+
+    assert.equal(input.getAttribute('aria-activedescendant'),
+        'chops-autocomplete-1-option-1');
+  });
+
+  it('hovering over a completion selects it', async () => {
+    element.completions = [
+      'hover',
+      'over',
+      'me',
+    ];
+
+    await element.updateComplete;
+
+    const completions = element.querySelectorAll('tr');
+
+    element._hoverCompletion({
+      currentTarget: completions[2],
+    });
+
+    assert.equal(element._selectedIndex, 2);
+
+    element._hoverCompletion({
+      currentTarget: completions[1],
+    });
+
+    assert.equal(element._selectedIndex, 1);
+  });
+
+  it('ArrowDown moves through completions', async () => {
+    element.completions = [
+      'move',
+      'down',
+      'me',
+    ];
+
+    element._selectedIndex = 0;
+
+    await element.updateComplete;
+
+    const preventDefault = sinon.stub();
+
+    element._navigateCompletions({preventDefault, key: 'ArrowDown'});
+    assert.equal(element._selectedIndex, 1);
+
+    element._navigateCompletions({preventDefault, key: 'ArrowDown'});
+    assert.equal(element._selectedIndex, 2);
+
+    // Wrap around.
+    element._navigateCompletions({preventDefault, key: 'ArrowDown'});
+    assert.equal(element._selectedIndex, 0);
+
+    sinon.assert.callCount(preventDefault, 3);
+  });
+
+  it('ArrowUp moves through completions', async () => {
+    element.completions = [
+      'move',
+      'up',
+      'me',
+    ];
+
+    element._selectedIndex = 0;
+
+    await element.updateComplete;
+
+    const preventDefault = sinon.stub();
+
+    // Wrap around.
+    element._navigateCompletions({preventDefault, key: 'ArrowUp'});
+    assert.equal(element._selectedIndex, 2);
+
+    element._navigateCompletions({preventDefault, key: 'ArrowUp'});
+    assert.equal(element._selectedIndex, 1);
+
+    element._navigateCompletions({preventDefault, key: 'ArrowUp'});
+    assert.equal(element._selectedIndex, 0);
+
+    sinon.assert.callCount(preventDefault, 3);
+  });
+
+  it('Enter completes with selected completion', async () => {
+    element.completions = [
+      'hello',
+      'pick me',
+      'world',
+    ];
+
+    element._selectedIndex = 1;
+
+    await element.updateComplete;
+
+    const preventDefault = sinon.stub();
+
+    element._navigateCompletions({preventDefault, key: 'Enter'});
+
+    assert.equal(input.value, 'pick me');
+    sinon.assert.callCount(preventDefault, 1);
+  });
+
+  it('Escape hides completions', async () => {
+    element.completions = [
+      'hide',
+      'me',
+    ];
+
+    await element.updateComplete;
+
+    const preventDefault = sinon.stub();
+    element._navigateCompletions({preventDefault, key: 'Escape'});
+
+    sinon.assert.callCount(preventDefault, 1);
+
+    await element.updateComplete;
+
+    assert.equal(element.completions.length, 0);
+  });
+});