Skip to content

Commit f726d14

Browse files
authored
Test/deflake label and autocomplete tests (#1950)
* test: deflake tests pfe-autocomplete and pfe-label tests would occasionally fail this commit aims to make those tests less flaky * feat(tools): add `reporter` option to test runner config
1 parent 4a30e8e commit f726d14

File tree

5 files changed

+118
-80
lines changed

5 files changed

+118
-80
lines changed

.changeset/nasty-swans-float.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@patternfly/pfe-tools": minor
3+
---
4+
5+
Add `reporter` option to test runner config factory

elements/pfe-autocomplete/test/pfe-autocomplete.spec.ts

Lines changed: 62 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { expect, oneEvent, nextFrame, html } from '@open-wc/testing';
1+
import { aTimeout, expect, oneEvent, nextFrame, html } from '@open-wc/testing';
22
import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js';
33
import { sendKeys } from '@web/test-runner-commands';
44
import * as sinon from 'sinon';
@@ -46,7 +46,7 @@ const TEMPLATES = {
4646
};
4747

4848
describe('<pfe-autocomplete>', function() {
49-
let autocompleteElem: PfeAutocomplete;
49+
let element: PfeAutocomplete;
5050
let input: HTMLInputElement;
5151
let clearButton: HTMLButtonElement;
5252
let searchButton: HTMLButtonElement;
@@ -55,12 +55,12 @@ describe('<pfe-autocomplete>', function() {
5555

5656
// function to run before each test within this suite.
5757
beforeEach(async function() {
58-
autocompleteElem = await createFixture<PfeAutocomplete>(TEMPLATES.autocomplete);
59-
clearButton = autocompleteElem.shadowRoot!.querySelector('.clear-search')!;
60-
searchButton = autocompleteElem.shadowRoot!.querySelector('.search-button')!;
61-
droplistElem = autocompleteElem.shadowRoot!.getElementById('dropdown') as PfeSearchDroplist;
62-
loadingIndicator = autocompleteElem.shadowRoot!.querySelector('.loading')!;
63-
input = autocompleteElem.querySelector('input')!;
58+
element = await createFixture<PfeAutocomplete>(TEMPLATES.autocomplete);
59+
clearButton = element.shadowRoot!.querySelector('.clear-search')!;
60+
searchButton = element.shadowRoot!.querySelector('.search-button')!;
61+
droplistElem = element.shadowRoot!.getElementById('dropdown') as PfeSearchDroplist;
62+
loadingIndicator = element.shadowRoot!.querySelector('.loading')!;
63+
input = element.querySelector('input')!;
6464
});
6565

6666
it('should upgrade pfe-autocomplete', async function() {
@@ -76,33 +76,33 @@ describe('<pfe-autocomplete>', function() {
7676

7777
it('should clear search when user press x button', async function() {
7878
input.value = 'search-term';
79-
autocompleteElem.clear();
79+
element.clear();
8080
expect(clearButton.hidden).to.be.true;
8181
});
8282

8383
// it('should close the overlay when user press x button', async function() { });
8484

8585
it('should close the overlay when user selects an option', async function() {
86-
autocompleteElem.data = ['option 1', 'option 2'];
86+
element.data = ['option 1', 'option 2'];
8787
droplistElem.reflow = true;
8888
droplistElem.open = true;
8989

9090
await droplistElem.updateComplete;
91-
await autocompleteElem.updateComplete;
91+
await element.updateComplete;
9292

9393
const option = droplistElem.shadowRoot?.querySelector<HTMLLIElement>('li:nth-child(2)');
9494
option?.click();
9595

9696
await droplistElem.updateComplete;
97-
await autocompleteElem.updateComplete;
97+
await element.updateComplete;
9898
await nextFrame();
9999

100100
expect(input.value).to.equal('option 2');
101101

102-
await autocompleteElem.clear();
102+
await element.clear();
103103

104104
await droplistElem.updateComplete;
105-
await autocompleteElem.updateComplete;
105+
await element.updateComplete;
106106

107107
expect(droplistElem.open).to.be.false;
108108
});
@@ -111,20 +111,20 @@ describe('<pfe-autocomplete>', function() {
111111
describe('should fire search event after click on search icon', function() {
112112
beforeEach(async function() {
113113
input.value = 'test';
114-
autocompleteElem.requestUpdate();
114+
element.requestUpdate();
115115
await droplistElem.updateComplete;
116-
await autocompleteElem.updateComplete;
116+
await element.updateComplete;
117117
setTimeout(() => searchButton.click());
118118
});
119119

120120
it('should fire search event', async function() {
121-
const event = await oneEvent(autocompleteElem, 'search') as unknown as AutocompleteSearchEvent;
121+
const event = await oneEvent(element, 'search') as unknown as AutocompleteSearchEvent;
122122
expect(event.value).to.equal('test');
123123
});
124124

125125
/** @deprecated */
126126
it('should fire pfe-autocomplete:search-event event', async function() {
127-
const eventDep = await oneEvent(autocompleteElem, 'pfe-autocomplete:search-event');
127+
const eventDep = await oneEvent(element, 'pfe-autocomplete:search-event');
128128
expect(eventDep.detail.searchValue).to.equal('test');
129129
});
130130
});
@@ -155,12 +155,12 @@ describe('<pfe-autocomplete>', function() {
155155

156156
it('should set selected-value attribute after after click on search icon', async function() {
157157
input.value = 'test';
158-
autocompleteElem.requestUpdate();
159-
await autocompleteElem.updateComplete;
158+
element.requestUpdate();
159+
await element.updateComplete;
160160
searchButton.click();
161-
await autocompleteElem.updateComplete;
161+
await element.updateComplete;
162162
await nextFrame();
163-
expect(autocompleteElem.getAttribute('selected-value')).to.eql('test');
163+
expect(element.getAttribute('selected-value')).to.eql('test');
164164
});
165165

166166
it('should set selected-value attribute after after click on search button', async function() {
@@ -177,29 +177,29 @@ describe('<pfe-autocomplete>', function() {
177177

178178
describe('should fire search after user click on an option', async function() {
179179
beforeEach(async function() {
180-
autocompleteElem.data = ['option 1', 'option 2'];
180+
element.data = ['option 1', 'option 2'];
181181
droplistElem.reflow = true;
182182
droplistElem.open = true;
183183
await droplistElem.updateComplete;
184184
});
185185
it('should fire search after user click on an option', async function() {
186186
const option = droplistElem.shadowRoot!.querySelector<HTMLElement>('li:nth-child(2)')!;
187187
setTimeout(()=> option.click());
188-
const event = await oneEvent(autocompleteElem, 'search') as unknown as AutocompleteSearchEvent;
188+
const event = await oneEvent(element, 'search') as unknown as AutocompleteSearchEvent;
189189
expect(event.value).to.equal('option 2');
190190
});
191191
/** @deprecated */
192192
it('should fire pfe-autocomplete:search-event after user click on an option', async function() {
193193
const option = droplistElem.shadowRoot!.querySelector<HTMLElement>('li:nth-child(2)')!;
194194
setTimeout(()=> option.click());
195-
const eventDep = await oneEvent(autocompleteElem, 'pfe-autocomplete:search-event');
195+
const eventDep = await oneEvent(element, 'pfe-autocomplete:search-event');
196196
expect(eventDep.detail.searchValue).to.equal('option 2');
197197
});
198198
});
199199

200200
describe('should fire a select event', function() {
201201
beforeEach(async function() {
202-
autocompleteElem.data = ['option 1', 'option 2'];
202+
element.data = ['option 1', 'option 2'];
203203
droplistElem.reflow = true;
204204
droplistElem.open = true;
205205
await nextFrame();
@@ -210,14 +210,14 @@ describe('<pfe-autocomplete>', function() {
210210
await sendKeys({ up: 'ArrowDown' });
211211
await nextFrame();
212212
setTimeout(() => sendKeys({ up: 'Enter' }));
213-
const event = await oneEvent(autocompleteElem, 'select') as unknown as AutocompleteSelectEvent;
213+
const event = await oneEvent(element, 'select') as unknown as AutocompleteSelectEvent;
214214
expect(event.value).to.equal('option 1');
215215
});
216216

217217
it(`should fire a pfe-autocomplete:option-selected event when a user selects an option in the droplist with the mouse`, async function() {
218218
const option = droplistElem.shadowRoot!.querySelector<HTMLElement>('li:nth-child(2)')!;
219219
setTimeout(() => option.click());
220-
const event = await oneEvent(autocompleteElem, 'pfe-autocomplete:option-selected');
220+
const event = await oneEvent(element, 'pfe-autocomplete:option-selected');
221221
expect(event.detail.optionValue).to.equal('option 2');
222222
});
223223
});
@@ -226,29 +226,29 @@ describe('<pfe-autocomplete>', function() {
226226
describe(`should fire a show event when the droplist is shown to the user`, async function() {
227227
beforeEach(function() {
228228
const items = ['option 1', 'option 2'];
229-
autocompleteElem.autocompleteRequest = function(_, callback) {
229+
element.autocompleteRequest = function(_, callback) {
230230
callback(items);
231231
};
232232
input.focus();
233233
setTimeout(() => sendKeys({ type: 'op' }));
234234
});
235235

236236
it(`should fire a shown event`, async function() {
237-
await oneEvent(autocompleteElem, 'show') as unknown as AutocompleteShowEvent;
237+
await oneEvent(element, 'show') as unknown as AutocompleteShowEvent;
238238
expect(droplistElem.hasAttribute('open')).to.be.true;
239239
});
240240

241241
/** @deprecated */
242242
it(`should fire a pfe-autocomplete:options-shown event`, async function() {
243-
await oneEvent(autocompleteElem, 'pfe-autocomplete:options-shown');
243+
await oneEvent(element, 'pfe-autocomplete:options-shown');
244244
expect(droplistElem.hasAttribute('open')).to.be.true;
245245
});
246246
});
247247

248248
describe(`should fire a clear event when the input is cleared`, async function() {
249249
beforeEach(async function() {
250250
const items = ['option 1', 'option 2'];
251-
autocompleteElem.autocompleteRequest = function(params, callback) {
251+
element.autocompleteRequest = function(params, callback) {
252252
const regx = new RegExp(`^${params.query}`, 'i');
253253
callback(items.filter(function(item) {
254254
return regx.test(item);
@@ -261,46 +261,46 @@ describe('<pfe-autocomplete>', function() {
261261
});
262262

263263
it(`should fire a clear event`, async function() {
264-
await oneEvent(autocompleteElem, 'pfe-autocomplete:option-cleared') as unknown as AutocompleteClearEvent;
264+
await oneEvent(element, 'pfe-autocomplete:option-cleared') as unknown as AutocompleteClearEvent;
265265
});
266266

267267
/** @deprecated */
268268
it(`should fire a pfe-autocomplete:option-cleared event`, async function() {
269-
await oneEvent(autocompleteElem, 'pfe-autocomplete:option-cleared');
269+
await oneEvent(element, 'pfe-autocomplete:option-cleared');
270270
});
271271
});
272272

273273
it('should set selected-value attribute after user click on an option', async function() {
274-
autocompleteElem.data = ['option 1', 'option 2'];
274+
element.data = ['option 1', 'option 2'];
275275
droplistElem.reflow = true;
276276
droplistElem.open = true;
277277
await nextFrame();
278278
const option = droplistElem.shadowRoot!.querySelector<HTMLElement>('li:nth-child(2)')!;
279279

280280
option.click();
281281
await nextFrame();
282-
expect(autocompleteElem.getAttribute('selected-value')).to.eql('option 2');
282+
expect(element.getAttribute('selected-value')).to.eql('option 2');
283283
});
284284

285285
it('should update inputbox value when setting the init-value', async function() {
286-
autocompleteElem.initValue = 'foo';
286+
element.initValue = 'foo';
287287
await nextFrame();
288288
expect(input.value).to.be.equal('foo');
289289
});
290290

291291
it('should add active class on first element on keydown when dropdown is open', async function() {
292-
autocompleteElem.data = ['option 1', 'option 2'];
292+
element.data = ['option 1', 'option 2'];
293293
droplistElem.reflow = true;
294294
droplistElem.open = true;
295295

296-
await autocompleteElem.updateComplete;
296+
await element.updateComplete;
297297
await droplistElem.updateComplete;
298298
await nextFrame();
299299

300300
input.focus();
301301
await sendKeys({ up: 'ArrowDown' });
302302

303-
await autocompleteElem.updateComplete;
303+
await element.updateComplete;
304304
await droplistElem.updateComplete;
305305
await nextFrame();
306306

@@ -310,7 +310,7 @@ describe('<pfe-autocomplete>', function() {
310310
});
311311

312312
it(`should add aria-selected true on first element on keydown when dropdown is open`, async function() {
313-
autocompleteElem.data = ['option 1', 'option 2'];
313+
element.data = ['option 1', 'option 2'];
314314
droplistElem.reflow = true;
315315
droplistElem.open = true;
316316

@@ -324,7 +324,7 @@ describe('<pfe-autocomplete>', function() {
324324
});
325325

326326
it('should set aria-expanded to true when dropdown is open', async function() {
327-
autocompleteElem.data = ['option 1', 'option 2'];
327+
element.data = ['option 1', 'option 2'];
328328
droplistElem.reflow = true;
329329
droplistElem.open = true;
330330

@@ -337,7 +337,7 @@ describe('<pfe-autocomplete>', function() {
337337
});
338338

339339
it('should update items list on mutation', async function() {
340-
autocompleteElem.data = ['option 1', 'option 2'];
340+
element.data = ['option 1', 'option 2'];
341341
droplistElem.reflow = true;
342342
droplistElem.open = true;
343343
await nextFrame();
@@ -346,7 +346,7 @@ describe('<pfe-autocomplete>', function() {
346346
});
347347

348348
it('hides dropdown content when an option is selected', async function() {
349-
autocompleteElem.data = ['option 1', 'option 2'];
349+
element.data = ['option 1', 'option 2'];
350350
droplistElem.reflow = true;
351351
droplistElem.open = true;
352352

@@ -360,7 +360,7 @@ describe('<pfe-autocomplete>', function() {
360360
});
361361

362362
it('hides dropdown content when an option is clicked', async function() {
363-
autocompleteElem.data = ['option 1', 'option 2'];
363+
element.data = ['option 1', 'option 2'];
364364
droplistElem.reflow = true;
365365
droplistElem.open = true;
366366
await nextFrame();
@@ -451,44 +451,48 @@ describe('<pfe-autocomplete>', function() {
451451
it('should display the loading indicator when open is true', async function() {
452452
await sendKeys({ press: 'Tab' });
453453
await sendKeys({ type: 'web components' });
454-
autocompleteElem.loading = true;
455-
await autocompleteElem.updateComplete;
454+
element.loading = true;
455+
await element.updateComplete;
456456
expect(loadingIndicator.hasAttribute('hidden')).to.be.false;
457457
});
458458

459459
it('should not display the loading indicator if the user has not typed in the input', async function() {
460460
await sendKeys({ press: 'Tab' });
461-
autocompleteElem.loading = true;
462-
await autocompleteElem.updateComplete;
461+
element.loading = true;
462+
await element.updateComplete;
463463
expect(loadingIndicator.hasAttribute('hidden')).to.be.true;
464464
});
465465
});
466466

467467
it('should call autocompleteRequest when the user types in the input', async function() {
468+
const TEST_EVENT_CB_CALLED = 'TEST_EVENT_CB_CALLED';
468469
const mockResults = ['web', 'components', 'web components', 'web development'];
469-
autocompleteElem.autocompleteRequest = function(_, callback) {
470-
autocompleteElem.loading = true;
470+
element.autocompleteRequest = function(_, callback) {
471+
element.loading = true;
471472
setTimeout(() => {
472473
callback(mockResults);
473-
autocompleteElem.loading = false;
474+
element.loading = false;
474475
}, 100);
476+
// fire an event that we'll listen for a few lines down in order to test the element state
477+
element.dispatchEvent(new Event(TEST_EVENT_CB_CALLED));
475478
};
476479
await sendKeys({ press: 'Tab' });
477480
await sendKeys({ type: 'web components' });
478481
// This is needed to account for the debounce delay in executing
479482
// the autocompleteRequest callback.
480-
await new Promise(resolve => setTimeout(resolve, 300));
483+
await oneEvent(element, TEST_EVENT_CB_CALLED);
481484
// Expect that the loading indicator is present and dropdown
482485
// list has not yet been opened
483-
expect(autocompleteElem.loading).to.be.true;
484-
expect(droplistElem.open).to.be.false;
486+
expect(element.loading, 'element is loading').to.be.true;
487+
await element.updateComplete;
488+
expect(droplistElem.open, 'droplist is closed').to.be.false;
485489

486490
// Now wait for the mockResults callback to execute
487-
await new Promise(resolve => setTimeout(resolve, 100));
491+
await aTimeout(100);
488492
// Expect that the loading indicator is gone and dropdown
489493
// list has been opened and populated with the correct data.
490-
expect(autocompleteElem.loading).to.be.false;
491-
expect(droplistElem.open).to.be.true;
494+
expect(element.loading, 'element is not loading').to.be.false;
495+
expect(droplistElem.open, 'droplist is open').to.be.true;
492496
// compare that the array values are equal.
493497
expect(droplistElem.data.join('')).to.equal(mockResults.join(''));
494498
});

elements/pfe-label/test/pfe-label.spec.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { expect, html, oneEvent } from '@open-wc/testing';
22
import { createFixture } from '@patternfly/pfe-tools/test/create-fixture.js';
33
import { PfeLabel } from '@patternfly/pfe-label';
44
import { getColor, hexToRgb } from '@patternfly/pfe-tools/test/hex-to-rgb.js';
5+
import { PfeIcon } from '@patternfly/pfe-icon';
56

67
const example = html`
78
<pfe-label></pfe-label>
@@ -29,6 +30,15 @@ const exampleWithIconAttributeEmpty = html`
2930

3031

3132
describe('<pfe-label>', function() {
33+
before(function() {
34+
// replace the default built-in icon set resolveIconName function
35+
// with one that loads local icons. we don't want tests dependent on
36+
// prod servers.
37+
PfeIcon.addIconSet('rh', '', function(name: string) {
38+
return `/elements/pfe-icon/test/${name.replace('rh', 'rh-icon')}.svg`;
39+
});
40+
});
41+
3242
it('should upgrade', async function() {
3343
const el = await createFixture<PfeLabel>(example);
3444
const klass = customElements.get('pfe-label');

0 commit comments

Comments
 (0)