Skip to content

Commit 31b9185

Browse files
committed
Add more unit tests
1 parent 73bbf69 commit 31b9185

File tree

2 files changed

+197
-17
lines changed

2 files changed

+197
-17
lines changed

src/labs/ia-combo-box/ia-combo-box.test.ts

Lines changed: 193 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const BASIC_OPTIONS = [
99
{ id: 'foo', text: 'Foo Option' },
1010
{ id: 'bar', text: 'Bar Option' },
1111
{ id: 'baz', text: 'Baz Option' },
12-
{ id: 'boop', text: 'Boop Option' },
12+
{ id: 'buzz', text: 'Buzz Option' },
1313
];
1414

1515
describe('IA Combo Box', () => {
@@ -112,12 +112,12 @@ describe('IA Combo Box', () => {
112112
></ia-combo-box>
113113
`);
114114

115-
const textInput = el.shadowRoot?.querySelector('#text-input') as HTMLInputElement;
115+
const textInput = el.shadowRoot?.querySelector(
116+
'#text-input',
117+
) as HTMLInputElement;
116118
expect(textInput).to.exist;
117119
expect(textInput.readOnly).to.be.true;
118120
});
119-
120-
// TODO ...
121121
});
122122

123123
describe('List behavior', () => {
@@ -134,12 +134,12 @@ describe('IA Combo Box', () => {
134134
<ia-combo-box behavior="list" .options=${BASIC_OPTIONS}></ia-combo-box>
135135
`);
136136

137-
const textInput = el.shadowRoot?.querySelector('#text-input') as HTMLInputElement;
137+
const textInput = el.shadowRoot?.querySelector(
138+
'#text-input',
139+
) as HTMLInputElement;
138140
expect(textInput).to.exist;
139141
expect(textInput.readOnly).to.be.false;
140142
});
141-
142-
// TODO ...
143143
});
144144

145145
describe('Freeform behavior', () => {
@@ -151,12 +151,12 @@ describe('IA Combo Box', () => {
151151
></ia-combo-box>
152152
`);
153153

154-
const textInput = el.shadowRoot?.querySelector('#text-input') as HTMLInputElement;
154+
const textInput = el.shadowRoot?.querySelector(
155+
'#text-input',
156+
) as HTMLInputElement;
155157
expect(textInput).to.exist;
156158
expect(textInput.readOnly).to.be.false;
157159
});
158-
159-
// TODO ...
160160
});
161161

162162
describe('Filtering presets', () => {
@@ -165,7 +165,89 @@ describe('IA Combo Box', () => {
165165
expect(el.filter).to.equal('substring');
166166
});
167167

168-
// TODO ...
168+
test('"all" filtering preset turns off filtering entirely', async () => {
169+
const el = await fixture<IAComboBox>(html`<ia-combo-box .options=${BASIC_OPTIONS} filter="all"></ia-combo-box>`);
170+
171+
const textInput = el.shadowRoot?.querySelector('#text-input') as HTMLInputElement;
172+
expect(textInput).to.exist;
173+
174+
textInput.value = 'b';
175+
textInput.dispatchEvent(new InputEvent('input'));
176+
await el.updateComplete;
177+
178+
// Options were not filtered
179+
const allOptionElmts = el.shadowRoot?.querySelectorAll('.option');
180+
expect(allOptionElmts?.length).to.equal(4);
181+
});
182+
183+
test('"prefix" filtering preset works correctly', async () => {
184+
const el = await fixture<IAComboBox>(html`<ia-combo-box .options=${BASIC_OPTIONS} filter="prefix"></ia-combo-box>`);
185+
186+
const textInput = el.shadowRoot?.querySelector('#text-input') as HTMLInputElement;
187+
expect(textInput).to.exist;
188+
189+
textInput.value = 'b';
190+
textInput.dispatchEvent(new InputEvent('input'));
191+
await el.updateComplete;
192+
193+
// Filtered options are 'Bar', 'Baz', and 'Buzz'
194+
const allOptionElmts = el.shadowRoot?.querySelectorAll('.option');
195+
expect(allOptionElmts?.length).to.equal(3);
196+
expect(allOptionElmts?.[0].textContent.trim()).to.equal('Bar');
197+
expect(allOptionElmts?.[1].textContent.trim()).to.equal('Baz');
198+
expect(allOptionElmts?.[2].textContent.trim()).to.equal('Buzz');
199+
});
200+
201+
test('"suffix" filtering preset works correctly', async () => {
202+
const el = await fixture<IAComboBox>(html`<ia-combo-box .options=${BASIC_OPTIONS} filter="suffix"></ia-combo-box>`);
203+
204+
const textInput = el.shadowRoot?.querySelector('#text-input') as HTMLInputElement;
205+
expect(textInput).to.exist;
206+
207+
textInput.value = 'b';
208+
textInput.dispatchEvent(new InputEvent('input'));
209+
await el.updateComplete;
210+
211+
// Filtered options are 'Baz' and 'Buzz'
212+
const allOptionElmts = el.shadowRoot?.querySelectorAll('.option');
213+
expect(allOptionElmts?.length).to.equal(2);
214+
expect(allOptionElmts?.[0].textContent.trim()).to.equal('Baz');
215+
expect(allOptionElmts?.[1].textContent.trim()).to.equal('Buzz');
216+
});
217+
218+
test('"substring" filtering preset works correctly', async () => {
219+
const el = await fixture<IAComboBox>(html`<ia-combo-box .options=${BASIC_OPTIONS} filter="substring"></ia-combo-box>`);
220+
221+
const textInput = el.shadowRoot?.querySelector('#text-input') as HTMLInputElement;
222+
expect(textInput).to.exist;
223+
224+
textInput.value = 'a';
225+
textInput.dispatchEvent(new InputEvent('input'));
226+
await el.updateComplete;
227+
228+
// Filtered options are 'Bar' and 'Baz'
229+
const allOptionElmts = el.shadowRoot?.querySelectorAll('.option');
230+
expect(allOptionElmts?.length).to.equal(2);
231+
expect(allOptionElmts?.[0].textContent.trim()).to.equal('Bar');
232+
expect(allOptionElmts?.[1].textContent.trim()).to.equal('Baz');
233+
});
234+
235+
test('"subsequence" filtering preset works correctly', async () => {
236+
const el = await fixture<IAComboBox>(html`<ia-combo-box .options=${BASIC_OPTIONS} filter="subsequence"></ia-combo-box>`);
237+
238+
const textInput = el.shadowRoot?.querySelector('#text-input') as HTMLInputElement;
239+
expect(textInput).to.exist;
240+
241+
textInput.value = 'bz';
242+
textInput.dispatchEvent(new InputEvent('input'));
243+
await el.updateComplete;
244+
245+
// Filtered options are 'Baz' and 'Buzz'
246+
const allOptionElmts = el.shadowRoot?.querySelectorAll('.option');
247+
expect(allOptionElmts?.length).to.equal(2);
248+
expect(allOptionElmts?.[0].textContent.trim()).to.equal('Baz');
249+
expect(allOptionElmts?.[1].textContent.trim()).to.equal('Buzz');
250+
});
169251
});
170252

171253
describe('Sorting behavior', () => {
@@ -174,11 +256,31 @@ describe('IA Combo Box', () => {
174256
expect(el.sort).to.be.false;
175257
});
176258

177-
// TODO ...
259+
test('shows options in provided order when sort=false', async () => {
260+
const el = await fixture<IAComboBox>(html`<ia-combo-box .options=${BASIC_OPTIONS} open></ia-combo-box>`);
261+
262+
const allOptionElmts = el.shadowRoot?.querySelectorAll('.option');
263+
expect(allOptionElmts?.length).to.equal(4);
264+
expect(allOptionElmts?.[0].textContent.trim()).to.equal('Foo');
265+
expect(allOptionElmts?.[1].textContent.trim()).to.equal('Bar');
266+
expect(allOptionElmts?.[2].textContent.trim()).to.equal('Baz');
267+
expect(allOptionElmts?.[3].textContent.trim()).to.equal('Buzz');
268+
});
269+
270+
test('shows options in lexicographic order when sort=true', async () => {
271+
const el = await fixture<IAComboBox>(html`<ia-combo-box .options=${BASIC_OPTIONS} sort open></ia-combo-box>`);
272+
273+
const allOptionElmts = el.shadowRoot?.querySelectorAll('.option');
274+
expect(allOptionElmts?.length).to.equal(4);
275+
expect(allOptionElmts?.[0].textContent.trim()).to.equal('Bar');
276+
expect(allOptionElmts?.[1].textContent.trim()).to.equal('Baz');
277+
expect(allOptionElmts?.[2].textContent.trim()).to.equal('Buzz');
278+
expect(allOptionElmts?.[3].textContent.trim()).to.equal('Foo');
279+
});
178280
});
179281

180-
describe('Keyboard navigation', () => {
181-
test('Enter key opens options list', async () => {
282+
describe('Common keyboard navigation', () => {
283+
test('Enter key opens options list w/o highlight', async () => {
182284
const el = await fixture<IAComboBox>(html`
183285
<ia-combo-box .options=${BASIC_OPTIONS}></ia-combo-box>
184286
`);
@@ -190,13 +292,74 @@ describe('IA Combo Box', () => {
190292

191293
textInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Enter' }));
192294
await el.updateComplete;
295+
expect(el.open).to.be.true;
193296

297+
// No option highlighted
298+
expect(el.shadowRoot?.querySelector('.highlight')).not.to.exist;
299+
});
300+
301+
test('Alt + Down key combo opens options list w/o highlight', async () => {
302+
const el = await fixture<IAComboBox>(html`
303+
<ia-combo-box .options=${BASIC_OPTIONS}></ia-combo-box>
304+
`);
305+
306+
const textInput = el.shadowRoot?.querySelector(
307+
'#text-input',
308+
) as HTMLInputElement;
309+
expect(textInput).to.exist;
310+
311+
textInput.dispatchEvent(
312+
new KeyboardEvent('keydown', { key: 'ArrowDown', altKey: true }),
313+
);
314+
await el.updateComplete;
194315
expect(el.open).to.be.true;
195316

196317
// No option highlighted
197318
expect(el.shadowRoot?.querySelector('.highlight')).not.to.exist;
198319
});
199320

321+
test('Down arrow opens options list and highlights first option', async () => {
322+
const el = await fixture<IAComboBox>(html`
323+
<ia-combo-box .options=${BASIC_OPTIONS}></ia-combo-box>
324+
`);
325+
326+
const textInput = el.shadowRoot?.querySelector(
327+
'#text-input',
328+
) as HTMLInputElement;
329+
expect(textInput).to.exist;
330+
331+
textInput.dispatchEvent(
332+
new KeyboardEvent('keydown', { key: 'ArrowDown' }),
333+
);
334+
await el.updateComplete;
335+
expect(el.open).to.be.true;
336+
337+
// Only the first option is highlighted
338+
const firstOption = el.shadowRoot?.querySelector('.option');
339+
expect(firstOption?.classList.contains('highlight')).to.be.true;
340+
expect(el.shadowRoot?.querySelectorAll('.highlight').length).to.equal(1);
341+
});
342+
343+
test('Up arrow opens options list and highlights last option', async () => {
344+
const el = await fixture<IAComboBox>(html`
345+
<ia-combo-box .options=${BASIC_OPTIONS}></ia-combo-box>
346+
`);
347+
348+
const textInput = el.shadowRoot?.querySelector(
349+
'#text-input',
350+
) as HTMLInputElement;
351+
expect(textInput).to.exist;
352+
353+
textInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'ArrowUp' }));
354+
await el.updateComplete;
355+
expect(el.open).to.be.true;
356+
357+
// Only the last option is highlighted
358+
const lastOption = el.shadowRoot?.querySelector('.option:last-of-type');
359+
expect(lastOption?.classList.contains('highlight')).to.be.true;
360+
expect(el.shadowRoot?.querySelectorAll('.highlight').length).to.equal(1);
361+
});
362+
200363
test('Escape key closes options list', async () => {
201364
const el = await fixture<IAComboBox>(html`
202365
<ia-combo-box .options=${BASIC_OPTIONS} open></ia-combo-box>
@@ -209,10 +372,24 @@ describe('IA Combo Box', () => {
209372

210373
textInput.dispatchEvent(new KeyboardEvent('keydown', { key: 'Escape' }));
211374
await el.updateComplete;
212-
213375
expect(el.open).to.be.false;
214376
});
215377

216-
// TODO ...
378+
test('Alt + Up key combo closes options list', async () => {
379+
const el = await fixture<IAComboBox>(html`
380+
<ia-combo-box .options=${BASIC_OPTIONS} open></ia-combo-box>
381+
`);
382+
383+
const textInput = el.shadowRoot?.querySelector(
384+
'#text-input',
385+
) as HTMLInputElement;
386+
expect(textInput).to.exist;
387+
388+
textInput.dispatchEvent(
389+
new KeyboardEvent('keydown', { key: 'ArrowUp', altKey: true }),
390+
);
391+
await el.updateComplete;
392+
expect(el.open).to.be.false;
393+
});
217394
});
218395
});

src/labs/ia-combo-box/ia-combo-box.ts

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -756,7 +756,10 @@ export class IAComboBox extends LitElement {
756756

757757
if (this.behavior === 'list') {
758758
this.setTextValue(this.selectedOption?.text ?? '');
759-
} else if (this.behavior === 'freeform' && (this.enteredText || this.value)) {
759+
} else if (
760+
this.behavior === 'freeform' &&
761+
(this.enteredText || this.value)
762+
) {
760763
this.setValue(this.enteredText);
761764
}
762765
}

0 commit comments

Comments
 (0)