import { test, expect } from '@playwright/test'; test('List fixtures cover normal, grouped, tree and virtual modes', async ({ page }) => { await page.goto('/test/list_test.html'); await expect(page.locator('#listStd .list-group-item').first()).toBeVisible(); await expect(page.locator('#listGrp .list-group-item').first()).toBeVisible(); await expect(page.locator('#listTree .list-group-item').first()).toBeVisible(); await expect(page.locator('#listFast .list-group-item').first()).toBeVisible(); const treeBefore = await page.locator('#listTree .list-group-item').count(); await page.locator('#listTree .bi-caret-down-fill').first().click(); await page.waitForTimeout(100); const treeAfter = await page.locator('#listTree .list-group-item').count(); expect(treeAfter).toBeLessThan(treeBefore); const fastInfo = await page.locator('#listFast').evaluate(async node => { const visibleText = () => Array.from(node.querySelectorAll('.list-group-item .fw-bold')).map(el => el.textContent.trim()); const initial = visibleText(); node.scrollTop = node.scrollHeight; node.dispatchEvent(new Event('scroll')); await new Promise(resolve => setTimeout(resolve, 300)); const final = visibleText(); return { count: node.querySelectorAll('.list-group-item').length, scrollTop: node.scrollTop, initialFirst: initial[0], finalFirst: final[0], finalLast: final[final.length - 1] }; }); expect(fastInfo.count).toBeGreaterThan(0); expect(fastInfo.count).toBeLessThan(400); expect(fastInfo.scrollTop).toBeGreaterThan(0); expect(fastInfo.initialFirst).not.toBe(fastInfo.finalFirst); expect(fastInfo.finalLast).toContain('Virtual Item'); }); test('List component supports internal filtering and sorting', async ({ page }) => { await page.goto('/test/list_test.html'); // 1. Test Standard List Filtering await page.fill('#searchInput', 'Item 5'); await page.waitForTimeout(100); const itemsText = await page.locator('#listStd .list-group-item').allTextContents(); // Item 5, Item 50 should be there, but not Item 1, 2, etc. expect(itemsText.every(text => text.includes('Item 5'))).toBe(true); expect(itemsText.length).toBe(2); // Item 5 and Item 50 // Clear filter await page.fill('#searchInput', ''); await page.waitForTimeout(100); // 2. Test Standard List Sorting // Default is: Item 1, Item 2... await page.selectOption('#sortSelect', '-label'); await page.waitForTimeout(100); const sortedText = await page.locator('#listStd .list-group-item').first().textContent(); expect(sortedText.trim()).toContain('Item 9'); // Lexicographically descending // 3. Test Group List Filtering await page.fill('#grpSearchInput', 'Member 3'); await page.waitForTimeout(100); // Since grpItems have label 'Member X' and group fields. Only member matching '3' or '30' or '31' etc. will show. const grpItemsCount = await page.locator('#listGrp .list-group-item').count(); // Should be filtered down considerably expect(grpItemsCount).toBeLessThan(40); // 4. Test Tree List Filtering (Crucial check for ancestry propagation) await page.fill('#treeSearchInput', 'UI Team'); await page.waitForTimeout(100); // UI Team should be visible const uiTeam = page.locator('#listTree .list-group-item', { hasText: 'UI Team' }); await expect(uiTeam).toBeVisible(); // Ancestor "Company HQ" and "R&D Division" must also be visible const parent1 = page.locator('#listTree .list-group-item', { hasText: 'Company HQ' }); const parent2 = page.locator('#listTree .list-group-item', { hasText: 'R&D Division' }); await expect(parent1).toBeVisible(); await expect(parent2).toBeVisible(); // Sibling node "Operations" or "Cloud Ops" should NOT be visible const sibling1 = page.locator('#listTree .list-group-item', { hasText: 'Operations' }); const sibling2 = page.locator('#listTree .list-group-item', { hasText: 'Cloud Ops' }); await expect(sibling1).not.toBeVisible(); await expect(sibling2).not.toBeVisible(); });