Project import generated by Copybara.
GitOrigin-RevId: d9e9e3fb4e31372ec1fb43b178994ca78fa8fe70
diff --git a/static_src/react/ReactAutocomplete.test.tsx b/static_src/react/ReactAutocomplete.test.tsx
new file mode 100644
index 0000000..a1e7c62
--- /dev/null
+++ b/static_src/react/ReactAutocomplete.test.tsx
@@ -0,0 +1,311 @@
+// Copyright 2021 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 {assert} from 'chai';
+import React from 'react';
+import sinon from 'sinon';
+import {fireEvent, render} from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+
+import {ReactAutocomplete, MAX_AUTOCOMPLETE_OPTIONS}
+ from './ReactAutocomplete.tsx';
+
+/**
+ * Cleans autocomplete dropdown from the DOM for the next test.
+ * @param input The autocomplete element to remove the dropdown for.
+ */
+ const cleanAutocomplete = (input: ReactAutocomplete) => {
+ fireEvent.change(input, {target: {value: ''}});
+ fireEvent.keyDown(input, {key: 'Enter', code: 'Enter'});
+};
+
+xdescribe('ReactAutocomplete', () => {
+ it('renders', async () => {
+ const {container} = render(<ReactAutocomplete label="cool" options={[]} />);
+
+ assert.isNotNull(container.querySelector('input'));
+ });
+
+ it('placeholder renders', async () => {
+ const {container} = render(<ReactAutocomplete
+ placeholder="penguins"
+ options={['']}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ assert.strictEqual(input?.placeholder, 'penguins');
+ });
+
+ it('filterOptions empty input value', async () => {
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ options={['option 1 label']}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ assert.strictEqual(input?.value, '');
+
+ fireEvent.keyDown(input, {key: 'Enter', code: 'Enter'});
+ assert.strictEqual(input?.value, '');
+ });
+
+ it('filterOptions truncates values', async () => {
+ const options = [];
+
+ // a0@test.com, a1@test.com, a2@test.com, ...
+ for (let i = 0; i <= MAX_AUTOCOMPLETE_OPTIONS; i++) {
+ options.push(`a${i}@test.com`);
+ }
+
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ options={options}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ userEvent.type(input, 'a');
+
+ const results = document.querySelectorAll('.autocomplete-option');
+
+ assert.equal(results.length, MAX_AUTOCOMPLETE_OPTIONS);
+
+ // Clean up autocomplete dropdown from the DOM for the next test.
+ cleanAutocomplete(input);
+ });
+
+ it('filterOptions label matching', async () => {
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ options={['option 1 label']}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ assert.strictEqual(input?.value, '');
+
+ userEvent.type(input, 'lab');
+ assert.strictEqual(input?.value, 'lab');
+
+ fireEvent.keyDown(input, {key: 'Enter', code: 'Enter'});
+
+ assert.strictEqual(input?.value, 'option 1 label');
+ });
+
+ it('filterOptions description matching', async () => {
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ getOptionDescription={() => 'penguin apples'}
+ options={['lol']}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ assert.strictEqual(input?.value, '');
+
+ userEvent.type(input, 'app');
+ assert.strictEqual(input?.value, 'app');
+
+ fireEvent.keyDown(input, {key: 'Enter', code: 'Enter'});
+ assert.strictEqual(input?.value, 'lol');
+ });
+
+ it('filterOptions no match', async () => {
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ options={[]}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ assert.strictEqual(input?.value, '');
+
+ userEvent.type(input, 'foobar');
+ assert.strictEqual(input?.value, 'foobar');
+
+ fireEvent.keyDown(input, {key: 'Enter', code: 'Enter'});
+ assert.strictEqual(input?.value, 'foobar');
+ });
+
+ it('onChange callback is called', async () => {
+ const onChangeStub = sinon.stub();
+
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ options={[]}
+ onChange={onChangeStub}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ sinon.assert.notCalled(onChangeStub);
+
+ userEvent.type(input, 'foobar');
+ sinon.assert.notCalled(onChangeStub);
+
+ fireEvent.keyDown(input, {key: 'Enter', code: 'Enter'});
+ sinon.assert.calledOnce(onChangeStub);
+
+ assert.equal(onChangeStub.getCall(0).args[1], 'foobar');
+ });
+
+ it('onChange excludes fixed values', async () => {
+ const onChangeStub = sinon.stub();
+
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ options={['cute owl']}
+ multiple={true}
+ fixedValues={['immortal penguin']}
+ onChange={onChangeStub}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ fireEvent.keyDown(input, {key: 'Backspace', code: 'Backspace'});
+ fireEvent.keyDown(input, {key: 'Enter', code: 'Enter'});
+
+ sinon.assert.calledWith(onChangeStub, sinon.match.any, []);
+ });
+
+ it('pressing space creates new chips', async () => {
+ const onChangeStub = sinon.stub();
+
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ options={['cute owl']}
+ multiple={true}
+ onChange={onChangeStub}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ sinon.assert.notCalled(onChangeStub);
+
+ userEvent.type(input, 'foobar');
+ sinon.assert.notCalled(onChangeStub);
+
+ fireEvent.keyDown(input, {key: ' ', code: 'Space'});
+ sinon.assert.calledOnce(onChangeStub);
+
+ assert.deepEqual(onChangeStub.getCall(0).args[1], ['foobar']);
+ });
+
+ it('_renderOption shows user input', async () => {
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ options={['cute@owl.com']}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ userEvent.type(input, 'ow');
+
+ const options = document.querySelectorAll('.autocomplete-option');
+
+ // Options: cute@owl.com
+ assert.deepEqual(options.length, 1);
+ assert.equal(options[0].textContent, 'cute@owl.com');
+
+ cleanAutocomplete(input);
+ });
+
+ it('_renderOption hides duplicate user input', async () => {
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ options={['cute@owl.com']}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ userEvent.type(input, 'cute@owl.com');
+
+ const options = document.querySelectorAll('.autocomplete-option');
+
+ // Options: cute@owl.com
+ assert.equal(options.length, 1);
+
+ assert.equal(options[0].textContent, 'cute@owl.com');
+
+ cleanAutocomplete(input);
+ });
+
+ it('_renderOption highlights matching text', async () => {
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ options={['cute@owl.com']}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ userEvent.type(input, 'ow');
+
+ const option = document.querySelector('.autocomplete-option');
+ const match = option?.querySelector('strong');
+
+ assert.isNotNull(match);
+ assert.equal(match?.innerText, 'ow');
+
+ // Description is not rendered.
+ assert.equal(option?.querySelectorAll('span').length, 1);
+ assert.equal(option?.querySelectorAll('strong').length, 1);
+
+ cleanAutocomplete(input);
+ });
+
+ it('_renderOption highlights matching description', async () => {
+ const {container} = render(<ReactAutocomplete
+ label="cool"
+ getOptionDescription={() => 'penguin of-doom'}
+ options={['cute owl']}
+ />);
+
+ const input = container.querySelector('input');
+ assert.isNotNull(input);
+ if (!input) return;
+
+ userEvent.type(input, 'do');
+
+ const option = document.querySelector('.autocomplete-option');
+ const match = option?.querySelector('strong');
+
+ assert.isNotNull(match);
+ assert.equal(match?.innerText, 'do');
+
+ assert.equal(option?.querySelectorAll('span').length, 2);
+ assert.equal(option?.querySelectorAll('strong').length, 1);
+
+ cleanAutocomplete(input);
+ });
+
+ it('_renderTags disables fixedValues', async () => {
+ // TODO(crbug.com/monorail/9393): Add this test once we have a way to stub
+ // out dependent components.
+ });
+});