Skip to content

Commit c2c87c5

Browse files
test(tooltip): add test cases (#21103)
* test(tooltip): add test cases * fix(definition-tooltip): storybook typos --------- Co-authored-by: Heloise Lui <[email protected]>
1 parent f033792 commit c2c87c5

File tree

3 files changed

+399
-4
lines changed

3 files changed

+399
-4
lines changed
Lines changed: 180 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,180 @@
1+
/**
2+
* Copyright IBM Corp. 2025
3+
*
4+
* This source code is licensed under the Apache-2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import '@carbon/web-components/es/components/tooltip/index.js';
9+
import { fixture, html, expect } from '@open-wc/testing';
10+
11+
describe('cds-definition-tooltip', () => {
12+
it('should support a custom class', async () => {
13+
const el = await fixture(html`
14+
<cds-definition-tooltip class="test-class">
15+
<span slot="definition">Example definition</span>
16+
URL
17+
</cds-definition-tooltip>
18+
`);
19+
expect(el.classList.contains('test-class')).to.be.true;
20+
});
21+
22+
it('should forward additional attributes on the outermost element', async () => {
23+
const el = await fixture(html`
24+
<cds-definition-tooltip data-testid="test-id">
25+
<span slot="definition">Example definition</span>
26+
URL
27+
</cds-definition-tooltip>
28+
>
29+
`);
30+
31+
expect(el).to.have.attribute('data-testid', 'test-id');
32+
});
33+
34+
it('should display the definition when clicked', async () => {
35+
const el = await fixture(html`
36+
<cds-definition-tooltip>
37+
<span slot="definition">Example definition</span>
38+
URL
39+
</cds-definition-tooltip>
40+
`);
41+
42+
const trigger = el.shadowRoot.querySelector('button');
43+
44+
expect(trigger).to.have.attribute('aria-expanded', 'false');
45+
46+
trigger.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
47+
await el.updateComplete;
48+
49+
expect(trigger).to.have.attribute('aria-expanded', 'true');
50+
});
51+
52+
it('should display the definition when focused', async () => {
53+
const el = await fixture(html`
54+
<cds-definition-tooltip>
55+
<span slot="definition">Example definition</span>
56+
URL
57+
</cds-definition-tooltip>
58+
`);
59+
60+
const trigger = el.shadowRoot.querySelector('button');
61+
62+
expect(trigger).to.have.attribute('aria-expanded', 'false');
63+
64+
trigger.dispatchEvent(new FocusEvent('focus', { bubbles: true }));
65+
await el.updateComplete;
66+
67+
expect(trigger).to.have.attribute('aria-expanded', 'true');
68+
});
69+
70+
it('should support initially showing the definition-tooltip with `defaultOpen`', async () => {
71+
const el = await fixture(html`
72+
<cds-definition-tooltip default-open>
73+
<span slot="definition">Example definition</span>
74+
URL
75+
</cds-definition-tooltip>
76+
>
77+
`);
78+
79+
expect(el.shadowRoot.querySelector('button')).to.have.attribute(
80+
'aria-expanded',
81+
'true'
82+
);
83+
});
84+
85+
it('should close the definition when Escape is clicked', async () => {
86+
const el = await fixture(html`
87+
<cds-definition-tooltip default-open>
88+
<span slot="definition">Example definition</span>
89+
URL
90+
</cds-definition-tooltip>
91+
`);
92+
93+
const trigger = el.shadowRoot.querySelector('button');
94+
expect(trigger).to.have.attribute('aria-expanded', 'true');
95+
96+
// press Escape
97+
trigger.dispatchEvent(
98+
new KeyboardEvent('keydown', {
99+
key: 'Escape',
100+
bubbles: true,
101+
})
102+
);
103+
104+
await el.updateComplete;
105+
106+
expect(trigger).to.have.attribute('aria-expanded', 'false');
107+
});
108+
109+
it('should close the definition when the trigger is blurred', async () => {
110+
const el = await fixture(html`
111+
<cds-definition-tooltip default-open>
112+
<span slot="definition">Example definition</span>
113+
URL
114+
</cds-definition-tooltip>
115+
`);
116+
117+
const trigger = el.shadowRoot.querySelector('button');
118+
expect(trigger).to.have.attribute('aria-expanded', 'true');
119+
120+
trigger.dispatchEvent(new FocusEvent('blur', { bubbles: true }));
121+
122+
await el.updateComplete;
123+
124+
expect(trigger).to.have.attribute('aria-expanded', 'false');
125+
});
126+
127+
it('should close on mouseleave', async () => {
128+
const el = await fixture(html`
129+
<cds-definition-tooltip default-open>
130+
<span slot="definition">Example definition</span>
131+
URL
132+
</cds-definition-tooltip>
133+
`);
134+
135+
const trigger = el.shadowRoot.querySelector('button');
136+
expect(trigger).to.have.attribute('aria-expanded', 'true');
137+
138+
trigger.dispatchEvent(new MouseEvent('mouseleave', { bubbles: true }));
139+
140+
await el.updateComplete;
141+
142+
expect(trigger).to.have.attribute('aria-expanded', 'false');
143+
});
144+
145+
it('should open on hover when `open-on-hover` is true', async () => {
146+
const el = await fixture(html`
147+
<cds-definition-tooltip open-on-hover>
148+
<span slot="definition">Example definition</span>
149+
URL
150+
</cds-definition-tooltip>
151+
`);
152+
153+
const trigger = el.shadowRoot.querySelector('button');
154+
expect(trigger).to.have.attribute('aria-expanded', 'false');
155+
156+
trigger.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
157+
158+
await el.updateComplete;
159+
160+
expect(trigger).to.have.attribute('aria-expanded', 'true');
161+
});
162+
163+
it('should not open on hover by default', async () => {
164+
const el = await fixture(html`
165+
<cds-definition-tooltip>
166+
<span slot="definition">Example definition</span>
167+
URL
168+
</cds-definition-tooltip>
169+
`);
170+
171+
const trigger = el.shadowRoot.querySelector('button');
172+
expect(trigger).to.have.attribute('aria-expanded', 'false');
173+
174+
trigger.dispatchEvent(new MouseEvent('mouseenter', { bubbles: true }));
175+
176+
await el.updateComplete;
177+
178+
expect(trigger).to.have.attribute('aria-expanded', 'false');
179+
});
180+
});
Lines changed: 215 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,215 @@
1+
/**
2+
* Copyright IBM Corp. 2025
3+
*
4+
* This source code is licensed under the Apache-2.0 license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import '@carbon/web-components/es/components/tooltip/index.js';
9+
import { fixture, html, expect } from '@open-wc/testing';
10+
11+
describe('cds-tooltip', () => {
12+
it('should support a custom class', async () => {
13+
const el = await fixture(
14+
html`<cds-tooltip class="test-class"
15+
><cds-tooltip-content>test</cds-tooltip-content></cds-tooltip
16+
>`
17+
);
18+
expect(el.classList.contains('test-class')).to.be.true;
19+
});
20+
21+
it('should forward additional attributes on the outermost element', async () => {
22+
const el = await fixture(html`
23+
<cds-tooltip data-testid="test-id"
24+
><cds-tooltip-content>test</cds-tooltip-content></cds-tooltip
25+
>
26+
`);
27+
28+
expect(el).to.have.attribute('data-testid', 'test-id');
29+
});
30+
31+
it('should support initially showing the tooltip with `defaultOpen`', async () => {
32+
const el = await fixture(html`
33+
<cds-tooltip defaultOpen>
34+
<button role="button" aria-labelledby="content"></button>
35+
<cds-tooltip-content id="content">
36+
Options
37+
</cds-tooltip-content></cds-tooltip
38+
>
39+
`);
40+
41+
const content = el.querySelector('cds-tooltip-content');
42+
expect(content).to.have.attribute('open');
43+
44+
const popoverContent = content.shadowRoot.querySelector(
45+
'.cds--popover-content'
46+
);
47+
const caret = content.shadowRoot.querySelector('.cds--popover-caret');
48+
expect(getComputedStyle(popoverContent).display).to.not.equal('none');
49+
expect(getComputedStyle(caret).display).to.not.equal('none');
50+
});
51+
52+
it('should close when item is activated and `closeOnActivation`', async () => {
53+
const el = await fixture(html`
54+
<cds-tooltip closeOnActivation defaultOpen leave-delay-ms="100">
55+
<button role="button" aria-labelledby="content"></button>
56+
<cds-tooltip-content id="content">
57+
Options
58+
</cds-tooltip-content></cds-tooltip
59+
>
60+
`);
61+
62+
const content = el.querySelector('cds-tooltip-content');
63+
expect(content).to.have.attribute('open');
64+
65+
const popoverContent = content.shadowRoot.querySelector(
66+
'.cds--popover-content'
67+
);
68+
const caret = content.shadowRoot.querySelector('.cds--popover-caret');
69+
expect(getComputedStyle(popoverContent).display).to.not.equal('none');
70+
expect(getComputedStyle(caret).display).to.not.equal('none');
71+
72+
// click the button
73+
const button = el.querySelector('button');
74+
button.click();
75+
await new Promise((resolve) => setTimeout(resolve, 100));
76+
await el.updateComplete;
77+
78+
expect(content).to.not.have.attribute('open');
79+
expect(getComputedStyle(popoverContent).display).to.equal('none');
80+
expect(getComputedStyle(caret).display).to.equal('none');
81+
});
82+
83+
it('should open when trigger button is focused and close when out of focus', async () => {
84+
const el = await fixture(html`
85+
<cds-tooltip enter-delay-ms="100" leave-delay-ms="300">
86+
<button role="button" aria-labelledby="content"></button>
87+
<cds-tooltip-content id="content">
88+
Options
89+
</cds-tooltip-content></cds-tooltip
90+
>
91+
`);
92+
const button = el.querySelector('button');
93+
const content = el.querySelector('cds-tooltip-content');
94+
const popoverContent = content.shadowRoot.querySelector(
95+
'.cds--popover-content'
96+
);
97+
const caret = content.shadowRoot.querySelector('.cds--popover-caret');
98+
99+
// tooltip should render as closed
100+
expect(content).to.not.have.attribute('open');
101+
expect(getComputedStyle(popoverContent).display).to.equal('none');
102+
expect(getComputedStyle(caret).display).to.equal('none');
103+
104+
// tooltip should open after enter-delay-ms
105+
button.dispatchEvent(new FocusEvent('focus', { bubbles: true }));
106+
await new Promise((resolve) => setTimeout(resolve, 100));
107+
await el.updateComplete;
108+
109+
expect(content).to.have.attribute('open');
110+
expect(getComputedStyle(popoverContent).display).to.not.equal('none');
111+
expect(getComputedStyle(caret).display).to.not.equal('none');
112+
113+
// tooltip should close after leave-delay-ms
114+
button.dispatchEvent(new FocusEvent('focusout', { bubbles: true }));
115+
await new Promise((resolve) => setTimeout(resolve, 300));
116+
await el.updateComplete;
117+
118+
expect(content).to.not.have.attribute('open');
119+
expect(getComputedStyle(popoverContent).display).to.equal('none');
120+
expect(getComputedStyle(caret).display).to.equal('none');
121+
});
122+
123+
it('should render with highContrast by default', async () => {
124+
const el = await fixture(html`
125+
<cds-tooltip defaultOpen>
126+
<button role="button" aria-labelledby="content"></button>
127+
<cds-tooltip-content id="content">
128+
Options
129+
</cds-tooltip-content></cds-tooltip
130+
>
131+
`);
132+
133+
expect(el).to.have.attribute('highContrast');
134+
});
135+
136+
it('should respect dropShadow attribute', async () => {
137+
const el = await fixture(html`
138+
<cds-tooltip dropShadow>
139+
<button role="button" aria-labelledby="content"></button>
140+
<cds-tooltip-content id="content">
141+
Options
142+
</cds-tooltip-content></cds-tooltip
143+
>
144+
`);
145+
await el.updateComplete;
146+
147+
const content = el.shadowRoot?.querySelector('[part="popover-container"]');
148+
const classList = content?.classList || [];
149+
expect(
150+
Array.from(classList).some((cls) =>
151+
cls.includes('--popover--drop-shadow')
152+
)
153+
).to.be.true;
154+
});
155+
156+
it('should pass `align`, `caret`, `autoalign`, `dropShadow`, attributes to the tooltip content', async () => {
157+
const el = await fixture(html`
158+
<cds-tooltip align="top" caret autoalign dropShadow>
159+
<button role="button" aria-labelledby="content"></button>
160+
<cds-tooltip-content id="content"> Options </cds-tooltip-content>
161+
</cds-tooltip>
162+
`);
163+
164+
const content = el.querySelector('cds-tooltip-content');
165+
166+
expect(content).to.exist;
167+
expect(content.align).to.equal('top');
168+
expect(content.caret).to.be.true;
169+
expect(content.dropShadow).to.be.true;
170+
expect(content.highContrast).to.be.true;
171+
expect(content.autoalign).to.be.true;
172+
});
173+
});
174+
175+
describe('cds-tooltip-content', () => {
176+
it('should support a custom class', async () => {
177+
const el = await fixture(
178+
html`<cds-tooltip
179+
><cds-tooltip-content class="test-class"
180+
>test</cds-tooltip-content
181+
></cds-tooltip
182+
>`
183+
);
184+
expect(
185+
el.querySelector('cds-tooltip-content').classList.contains('test-class')
186+
).to.be.true;
187+
});
188+
189+
it('should forward additional attributes on the outermost element', async () => {
190+
const el = await fixture(html`
191+
<cds-tooltip
192+
><cds-tooltip-content data-testid="test-id"
193+
>test</cds-tooltip-content
194+
></cds-tooltip
195+
>
196+
`);
197+
198+
expect(el.querySelector('cds-tooltip-content')).to.have.attribute(
199+
'data-testid',
200+
'test-id'
201+
);
202+
});
203+
204+
it('should add tooltip-content class to popover-content', async () => {
205+
const el = await fixture(html`
206+
<cds-tooltip><cds-tooltip-content>test</cds-tooltip-content></cds-tooltip>
207+
`);
208+
const popoverContent = el
209+
.querySelector('cds-tooltip-content')
210+
.shadowRoot.querySelector('.cds--popover-content');
211+
212+
expect(popoverContent.classList.contains('cds--tooltip-content')).to.be
213+
.true;
214+
});
215+
});

0 commit comments

Comments
 (0)