Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit f2da970

Browse files
committed
chore(core): test coverage
Signed-off-by: Cory Rylan <[email protected]>
1 parent 471b568 commit f2da970

23 files changed

Lines changed: 547 additions & 18 deletions

File tree

projects/core/src/accordion/accordion.test.ts

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,14 @@ describe(`${Accordion.metadata.tag} - Actions`, () => {
274274
it('should align caret icon button to left side if an action is provided by consumer', () => {
275275
expect(element.shadowRoot.querySelector('.has-action')).toBeTruthy();
276276
});
277+
278+
it('should keep the action caret pointing down when expanded', async () => {
279+
element.expanded = true;
280+
await elementIsStable(element);
281+
282+
const iconButton = element.shadowRoot.querySelector<IconButton>(IconButton.metadata.tag);
283+
expect(iconButton.direction).toBe('down');
284+
});
277285
});
278286

279287
describe(`${Accordion.metadata.tag} - inline interactive`, () => {

projects/core/src/alert/alert.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,13 +53,27 @@ describe(Alert.metadata.tag, () => {
5353
expect(alert.shadowRoot.querySelector<Icon>(Icon.metadata.tag).name).toBe('checkmark-circle');
5454
});
5555

56+
it('should use default status icon for unsupported status values', async () => {
57+
alert.status = 'unsupported' as Alert['status'];
58+
await elementIsStable(alert);
59+
60+
expect(alert.shadowRoot.querySelector<Icon>(Icon.metadata.tag).name).toBe('information-circle-stroke');
61+
});
62+
5663
it('should set an aria-label for the icon status', async () => {
5764
expect(alert.shadowRoot.querySelector<Icon>(Icon.metadata.tag).ariaLabel).toBe('information');
5865
alert.status = 'success';
5966
await elementIsStable(alert);
6067
expect(alert.shadowRoot.querySelector<Icon>(Icon.metadata.tag).ariaLabel).toBe('success');
6168
});
6269

70+
it('should use hardcoded icon aria-label fallback when i18n is missing', async () => {
71+
alert.i18n = {} as Alert['i18n'];
72+
await elementIsStable(alert);
73+
74+
expect(alert.shadowRoot.querySelector<Icon>(Icon.metadata.tag).ariaLabel).toBe('information');
75+
});
76+
6377
it('should provide a aria role of alert to describe content', async () => {
6478
await elementIsStable(alert);
6579
expect(alert._internals.role).toBe('alert');

projects/core/src/color/color.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,20 @@ describe(Color.metadata.tag, () => {
4040
expect(fixture.querySelector('input').value).toBe('#dde1e4');
4141
});
4242

43+
it('should ignore non-hex background defaults', async () => {
44+
removeFixture(fixture);
45+
fixture = await createFixture(html`
46+
<nve-color style="--background: rgb(221, 225, 228)">
47+
<label>label</label>
48+
<input type="color" />
49+
</nve-color>
50+
`);
51+
element = fixture.querySelector(Color.metadata.tag);
52+
await elementIsStable(element);
53+
54+
expect(fixture.querySelector('input').value).toBe('#000000');
55+
});
56+
4357
it('should apply default if custom default is provided', async () => {
4458
await elementIsStable(element);
4559
expect(fixture.querySelector('input').value).toBe('#dde1e4');

projects/core/src/divider/divider.test.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,4 +56,11 @@ describe(Divider.metadata.tag, () => {
5656
await elementIsStable(element);
5757
expect(element._internals.ariaOrientation).toBe('horizontal');
5858
});
59+
60+
it('should preserve ariaOrientation when orientation is unchanged', async () => {
61+
element.requestUpdate();
62+
await elementIsStable(element);
63+
64+
expect(element._internals.ariaOrientation).toBe('horizontal');
65+
});
5966
});

projects/core/src/dropdown-group/dropdown-group.test.ts

Lines changed: 30 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -64,11 +64,9 @@ describe(DropdownGroup.metadata.tag, () => {
6464
const dropdown1 = element.dropdowns[0];
6565
const dropdown2 = element.dropdowns[1];
6666

67-
// Open both dropdowns
6867
dropdown1.showPopover();
6968
dropdown2.showPopover();
7069

71-
// Simulate click outside
7270
const event = new PointerEvent('pointerup', {
7371
clientX: 1000,
7472
clientY: 1000
@@ -81,6 +79,26 @@ describe(DropdownGroup.metadata.tag, () => {
8179
expect(dropdown2.matches(':popover-open')).toBe(false);
8280
});
8381

82+
it('should keep dropdowns open when clicking inside the group', async () => {
83+
const dropdown1 = element.dropdowns[0];
84+
const dropdown2 = element.dropdowns[1];
85+
dropdown1.showPopover();
86+
dropdown2.showPopover();
87+
88+
dropdown1.dispatchEvent(
89+
new PointerEvent('pointerup', {
90+
bubbles: true,
91+
clientX: dropdown1.getBoundingClientRect().left,
92+
clientY: dropdown1.getBoundingClientRect().top
93+
})
94+
);
95+
96+
await elementIsStable(element);
97+
98+
expect(dropdown1.matches(':popover-open')).toBe(true);
99+
expect(dropdown2.matches(':popover-open')).toBe(true);
100+
});
101+
84102
it('should handle keyboard navigation', async () => {
85103
const dropdown1 = element.dropdowns[0];
86104
const dropdown2 = element.dropdowns[1];
@@ -155,26 +173,31 @@ describe(DropdownGroup.metadata.tag, () => {
155173
const dropdown1 = element.dropdowns[0];
156174
const button = fixture.querySelector('nve-button');
157175

158-
// Open dropdown
159176
dropdown1.showPopover();
160177
await elementIsStable(element);
161178

162-
// Hide popover first
163179
dropdown1.hidePopover();
164180
await elementIsStable(element);
165181

166-
// Trigger close event
167182
const closeEvent = new CustomEvent('close', { bubbles: true });
168183
dropdown1.dispatchEvent(closeEvent);
169184

170-
// Wait for focus to be set
171185
await new Promise(resolve => setTimeout(resolve, 50));
172186
await elementIsStable(element);
173187

174-
// Ensure button is focusable
175188
button.setAttribute('tabindex', '0');
176189
button.focus();
177190

178191
expect(document.activeElement).toBe(button);
179192
});
193+
194+
it('should ignore open and close events from non-local dropdowns', async () => {
195+
const activeElement = document.activeElement;
196+
element.dispatchEvent(new CustomEvent('open', { bubbles: true }));
197+
element.dispatchEvent(new CustomEvent('close', { bubbles: true }));
198+
199+
await elementIsStable(element);
200+
201+
expect(document.activeElement).toBe(activeElement);
202+
});
180203
});

projects/core/src/format-datetime/format-datetime.test.ts

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,17 @@ describe(FormatDatetime.metadata.tag, () => {
9393
expect(text).toContain('7/28/23');
9494
expect(text).toMatch(/\d{1,2}:\d{2}\s*(AM|PM)/);
9595
});
96+
97+
it('should format with time-style only', async () => {
98+
element.removeAttribute('date-style');
99+
element.dateStyle = undefined;
100+
element.timeStyle = 'short';
101+
element.timeZone = undefined;
102+
await elementIsStable(element);
103+
104+
const time = element.shadowRoot!.querySelector('time');
105+
expect(time!.textContent!.trim()).toMatch(/\d{1,2}:\d{2}\s*(AM|PM)/);
106+
});
96107
});
97108

98109
describe('granular options', () => {
@@ -141,6 +152,20 @@ describe(FormatDatetime.metadata.tag, () => {
141152
const time = element.shadowRoot!.querySelector('time');
142153
expect(time!.textContent!.trim()).toMatch(/\d{1,2}:\d{2}\s*(AM|PM)/);
143154
});
155+
156+
it('should format seconds without a time zone', async () => {
157+
fixture.innerHTML = '';
158+
removeFixture(fixture);
159+
160+
fixture = await createFixture(
161+
html`<nve-format-datetime locale="en-US" second="2-digit">2023-07-28T04:20:17.434Z</nve-format-datetime>`
162+
);
163+
element = fixture.querySelector(FormatDatetime.metadata.tag);
164+
await elementIsStable(element);
165+
166+
const time = element.shadowRoot!.querySelector('time');
167+
expect(time!.textContent!.trim()).toBe('17');
168+
});
144169
});
145170

146171
describe('locale', () => {
@@ -159,6 +184,21 @@ describe(FormatDatetime.metadata.tag, () => {
159184
expect(time!.textContent!.trim()).toContain('Juli');
160185
});
161186

187+
it('should use the browser locale when locale and document lang are omitted', async () => {
188+
document.documentElement.lang = '';
189+
fixture.innerHTML = '';
190+
removeFixture(fixture);
191+
192+
fixture = await createFixture(
193+
html`<nve-format-datetime date-style="long" time-zone="UTC">2023-07-28T04:20:17.434Z</nve-format-datetime>`
194+
);
195+
element = fixture.querySelector(FormatDatetime.metadata.tag);
196+
await elementIsStable(element);
197+
198+
const time = element.shadowRoot!.querySelector('time');
199+
expect(time!.textContent!.trim()).toBe('July 28, 2023');
200+
});
201+
162202
it('should format with de-DE locale', async () => {
163203
fixture.innerHTML = '';
164204
removeFixture(fixture);

projects/core/src/format-number/format-number.test.ts

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,24 @@ describe(FormatNumber.metadata.tag, () => {
202202
const data = element.shadowRoot!.querySelector('data');
203203
expect(data!.textContent!.trim()).toBe('1234567');
204204
});
205+
206+
it('should force grouping with true', async () => {
207+
element.number = '1234567';
208+
element.useGrouping = 'true';
209+
await elementIsStable(element);
210+
211+
const data = element.shadowRoot!.querySelector('data');
212+
expect(data!.textContent!.trim()).toBe('1,234,567');
213+
});
214+
215+
it('should pass through Intl grouping options', async () => {
216+
element.number = '1234567';
217+
element.useGrouping = 'always';
218+
await elementIsStable(element);
219+
220+
const data = element.shadowRoot!.querySelector('data');
221+
expect(data!.textContent!.trim()).toBe('1,234,567');
222+
});
205223
});
206224

207225
describe('fraction digits', () => {
@@ -237,6 +255,18 @@ describe(FormatNumber.metadata.tag, () => {
237255
expect(data!.textContent!.trim()).toBe('1.234,56');
238256
});
239257

258+
it('should fall back to browser locale when document lang is empty', async () => {
259+
document.documentElement.lang = '';
260+
removeFixture(fixture);
261+
262+
fixture = await createFixture(html`<nve-format-number>1234.56</nve-format-number>`);
263+
element = fixture.querySelector(FormatNumber.metadata.tag);
264+
await elementIsStable(element);
265+
266+
const data = element.shadowRoot!.querySelector('data');
267+
expect(data!.textContent!.trim()).toBe(new Intl.NumberFormat(undefined).format(1234.56));
268+
});
269+
240270
it('should format with explicit de-DE locale', async () => {
241271
removeFixture(fixture);
242272

@@ -312,5 +342,19 @@ describe(FormatNumber.metadata.tag, () => {
312342
expect(data!.textContent!.trim()).toBe('');
313343
expect(warnSpy).not.toHaveBeenCalled();
314344
});
345+
346+
it('should render empty when text content is unavailable', async () => {
347+
Object.defineProperty(element, 'textContent', {
348+
configurable: true,
349+
get: () => undefined
350+
});
351+
element.requestUpdate();
352+
await elementIsStable(element);
353+
354+
const data = element.shadowRoot!.querySelector('data');
355+
expect(data!.getAttribute('value')).toBe('');
356+
expect(data!.textContent!.trim()).toBe('');
357+
expect(warnSpy).not.toHaveBeenCalled();
358+
});
315359
});
316360
});

projects/core/src/format-relative-time/format-relative-time.test.ts

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -215,6 +215,20 @@ describe(FormatRelativeTime.metadata.tag, () => {
215215
expect(time!.textContent!.trim()).toContain('Tag');
216216
});
217217

218+
it('should use the browser locale when locale and document lang are omitted', async () => {
219+
document.documentElement.lang = '';
220+
removeFixture(fixture);
221+
222+
fixture = await createFixture(
223+
html`<nve-format-relative-time date="2023-07-27T12:00:00.000Z"></nve-format-relative-time>`
224+
);
225+
element = fixture.querySelector(FormatRelativeTime.metadata.tag);
226+
await elementIsStable(element);
227+
228+
const time = element.shadowRoot!.querySelector('time');
229+
expect(time!.textContent!.trim()).toBe('1 day ago');
230+
});
231+
218232
it('should format with de-DE locale', async () => {
219233
removeFixture(fixture);
220234

@@ -305,6 +319,49 @@ describe(FormatRelativeTime.metadata.tag, () => {
305319
expect(element.shadowRoot!.querySelector('time')!.textContent!.trim()).toBe('2 minutes ago');
306320
});
307321

322+
it('should start sync when connected with the sync attribute', async () => {
323+
removeFixture(fixture);
324+
325+
fixture = await createFixture(
326+
html`<nve-format-relative-time locale="en-US" sync date="2023-07-28T11:59:30.000Z"></nve-format-relative-time>`
327+
);
328+
element = fixture.querySelector(FormatRelativeTime.metadata.tag);
329+
await elementIsStable(element);
330+
331+
vi.setSystemTime(new Date('2023-07-28T12:01:00.000Z'));
332+
vi.advanceTimersByTime(10000);
333+
await elementIsStable(element);
334+
335+
expect(element.shadowRoot!.querySelector('time')!.textContent!.trim()).toBe('2 minutes ago');
336+
});
337+
338+
it('should use the hourly sync interval for day-or-greater distances', async () => {
339+
element.date = '2023-07-26T12:00:00.000Z';
340+
element.sync = true;
341+
await elementIsStable(element);
342+
343+
vi.advanceTimersByTime(300000);
344+
await elementIsStable(element);
345+
expect(element.shadowRoot!.querySelector('time')!.textContent!.trim()).toBe('2 days ago');
346+
347+
vi.advanceTimersByTime(3600000);
348+
await elementIsStable(element);
349+
expect(element.shadowRoot!.querySelector('time')!.textContent!.trim()).toBe('2 days ago');
350+
});
351+
352+
it('should use the default sync interval when the date is empty', async () => {
353+
removeFixture(fixture);
354+
355+
fixture = await createFixture(html`<nve-format-relative-time locale="en-US" sync></nve-format-relative-time>`);
356+
element = fixture.querySelector(FormatRelativeTime.metadata.tag);
357+
await elementIsStable(element);
358+
359+
vi.advanceTimersByTime(60000);
360+
await elementIsStable(element);
361+
362+
expect(element.shadowRoot!.querySelector('time')!.textContent!.trim()).toBe('');
363+
});
364+
308365
it('should adapt interval as time distance grows', async () => {
309366
element.date = '2023-07-28T11:59:50.000Z';
310367
element.sync = true;

projects/core/src/forms/control-group/control-group.test.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -61,4 +61,14 @@ describe(ControlGroup.metadata.tag, () => {
6161
await elementIsStable(element);
6262
expect(element.shadowRoot.querySelector('.no-label')).toBeTruthy();
6363
});
64+
65+
it('should fall back to empty control collections when querySelectorAll is unavailable', async () => {
66+
Object.defineProperty(element, 'querySelectorAll', { value: undefined, configurable: true });
67+
68+
expect(element.inputs).toEqual([]);
69+
70+
element.requestUpdate();
71+
await elementIsStable(element);
72+
expect(element.shadowRoot.querySelector('.no-messages')).toBeTruthy();
73+
});
6474
});

projects/core/src/icon/icon.static.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,4 +30,18 @@ describe(`${Icon.metadata.tag}: static`, () => {
3030
mergeIcons(Registered as unknown as typeof Icon);
3131
expect(Registered._icons['merge-svg-2']).toBeDefined();
3232
});
33+
34+
it('should not merge icons for newer registered versions', () => {
35+
class Registered {
36+
static metadata = {
37+
version: '1.0.0'
38+
};
39+
40+
static _icons: Record<string, { svg: () => string }> = {};
41+
}
42+
43+
mergeIcons(Registered as unknown as typeof Icon);
44+
45+
expect(Registered._icons.book).toBeUndefined();
46+
});
3347
});

0 commit comments

Comments
 (0)