Skip to content

Commit 137106c

Browse files
committed
test(menu): add a bunch of tests, primarily for testing keyboard navigation
1 parent 31b2d97 commit 137106c

File tree

2 files changed

+195
-0
lines changed

2 files changed

+195
-0
lines changed

src/components/menu/menu.e2e.ts

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -138,3 +138,192 @@ describe('limel-menu', () => {
138138
});
139139
});
140140
});
141+
142+
describe('limel-menu keyboard navigation', () => {
143+
describe('with nested items (sub-menus)', () => {
144+
let page: E2EPage;
145+
let limelMenu: HTMLLimelMenuElement & E2EElement;
146+
147+
beforeEach(async () => {
148+
page = await newE2EPage({
149+
html: `
150+
<limel-menu>
151+
<button slot="trigger">Open Menu</button>
152+
</limel-menu>
153+
`,
154+
});
155+
limelMenu = (await page.find('limel-menu')) as any;
156+
const items = [
157+
{
158+
text: 'Parent Item',
159+
items: [{ text: 'Child Item 1' }, { text: 'Child Item 2' }],
160+
},
161+
{ text: 'Another Item' },
162+
];
163+
await limelMenu.setProperty('items', items);
164+
await page.waitForChanges();
165+
});
166+
167+
it('right arrow on item with sub-menu emits navigateMenu', async () => {
168+
// Click the trigger button to open the menu (this sets up proper focus)
169+
const trigger = await page.find('button');
170+
await trigger.click();
171+
await page.waitForChanges();
172+
await page.waitForTimeout(300);
173+
174+
const navigateMenuSpy = await page.spyOnEvent('navigateMenu');
175+
176+
// Press right arrow to navigate into sub-menu (first item is focused by default)
177+
await page.keyboard.press('ArrowRight');
178+
await page.waitForChanges();
179+
await page.waitForTimeout(100);
180+
181+
expect(navigateMenuSpy).toHaveReceivedEvent();
182+
});
183+
184+
it('left arrow in sub-menu goes back to parent', async () => {
185+
// Click to open the menu
186+
const trigger = await page.find('button');
187+
await trigger.click();
188+
await page.waitForChanges();
189+
await page.waitForTimeout(300);
190+
191+
// Enter sub-menu with ArrowRight
192+
await page.keyboard.press('ArrowRight');
193+
await page.waitForChanges();
194+
await page.waitForTimeout(200);
195+
196+
// Verify we're in sub-menu
197+
let currentSubMenu = await limelMenu.getProperty('currentSubMenu');
198+
expect(currentSubMenu).not.toBeNull();
199+
200+
// Press left to go back
201+
await page.keyboard.press('ArrowLeft');
202+
await page.waitForChanges();
203+
await page.waitForTimeout(100);
204+
205+
// Verify we're back at root
206+
currentSubMenu = await limelMenu.getProperty('currentSubMenu');
207+
expect(currentSubMenu).toBeNull();
208+
});
209+
210+
it('breadcrumbs visible when in sub-menu', async () => {
211+
// Click to open the menu
212+
const trigger = await page.find('button');
213+
await trigger.click();
214+
await page.waitForChanges();
215+
await page.waitForTimeout(300);
216+
217+
// Navigate into sub-menu
218+
await page.keyboard.press('ArrowRight');
219+
await page.waitForChanges();
220+
await page.waitForTimeout(300);
221+
222+
// Verify we're actually in the sub-menu
223+
const currentSubMenu =
224+
await limelMenu.getProperty('currentSubMenu');
225+
expect(currentSubMenu).not.toBeNull();
226+
227+
// Breadcrumbs are rendered in a portal (outside the menu's shadow DOM)
228+
// so we need to look for them in the document
229+
const breadcrumbsExists = await page.evaluate(() => {
230+
// First try looking in the portal container
231+
const portalContainer = document.querySelector(
232+
'.limel-portal--container'
233+
);
234+
if (
235+
portalContainer?.querySelector(
236+
'limel-breadcrumbs, [class*="breadcrumb"]'
237+
)
238+
) {
239+
return true;
240+
}
241+
242+
// Check inside limel-menu-surface
243+
const menuSurface =
244+
document.querySelector('limel-menu-surface');
245+
if (menuSurface) {
246+
const breadcrumbs =
247+
menuSurface.querySelector('limel-breadcrumbs');
248+
if (breadcrumbs) {
249+
return true;
250+
}
251+
}
252+
253+
// Also check if breadcrumbs exist anywhere in the document
254+
return document.querySelector('limel-breadcrumbs') !== null;
255+
});
256+
expect(breadcrumbsExists).toBe(true);
257+
});
258+
});
259+
260+
describe('without searcher (no input field)', () => {
261+
let page: E2EPage;
262+
let limelMenu: HTMLLimelMenuElement & E2EElement;
263+
264+
beforeEach(async () => {
265+
page = await newE2EPage({
266+
html: `
267+
<limel-menu>
268+
<button slot="trigger">Open Menu</button>
269+
</limel-menu>
270+
`,
271+
});
272+
limelMenu = (await page.find('limel-menu')) as any;
273+
const items = [
274+
{ text: 'Item 1' },
275+
{ text: 'Item 2' },
276+
{ text: 'Item 3' },
277+
];
278+
await limelMenu.setProperty('items', items);
279+
await page.waitForChanges();
280+
});
281+
282+
it('no input field when no searcher is set', async () => {
283+
const trigger = await page.find('button');
284+
await trigger.click();
285+
await page.waitForChanges();
286+
await page.waitForTimeout(200);
287+
288+
const inputExists = await page.evaluate(() => {
289+
const menu = document.querySelector('limel-menu');
290+
const inputField =
291+
menu?.shadowRoot?.querySelector('limel-input-field');
292+
293+
return inputField !== null;
294+
});
295+
expect(inputExists).toBe(false);
296+
});
297+
298+
it('focus wraps within list items when no input field', async () => {
299+
const trigger = await page.find('button');
300+
await trigger.click();
301+
await page.waitForChanges();
302+
await page.waitForTimeout(200);
303+
304+
// Navigate down from first item (Item 1 -> Item 2 -> Item 3)
305+
await page.keyboard.press('ArrowDown');
306+
await page.keyboard.press('ArrowDown');
307+
await page.waitForChanges();
308+
await page.waitForTimeout(50);
309+
310+
// Press down once more - should wrap to first item (Item 3 -> Item 1)
311+
await page.keyboard.press('ArrowDown');
312+
await page.waitForChanges();
313+
await page.waitForTimeout(50);
314+
315+
const firstItemFocused = await page.evaluate(() => {
316+
const menu = document.querySelector('limel-menu');
317+
const menuList =
318+
menu?.shadowRoot?.querySelector('limel-menu-list');
319+
const listItems = menuList?.shadowRoot?.querySelectorAll(
320+
'.mdc-deprecated-list-item'
321+
);
322+
const firstItem = listItems?.[0];
323+
324+
return firstItem === menuList?.shadowRoot?.activeElement;
325+
});
326+
expect(firstItemFocused).toBe(true);
327+
});
328+
});
329+
});

src/test-assets/icons/home.svg

Lines changed: 6 additions & 0 deletions
Loading

0 commit comments

Comments
 (0)