import { test, expect } from '@playwright/test'; test.describe('Advanced Search and Filtering', () => { test.describe('Global Search', () => { test('can perform global search', async ({ page }) => { await page.goto('/dashboard'); const searchInput = page.locator('input[type="search"], [data-global-search]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('test'); await page.waitForTimeout(1000); // Results should appear const results = page.locator('[data-search-results], [role="listbox"]').first(); if (await results.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(results).toBeVisible(); } } }); test('global search shows categorized results', async ({ page }) => { await page.goto('/dashboard'); const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('device'); await page.waitForTimeout(1000); // Look for category headers (Devices, Sites, Alerts, etc.) const category = page.locator(':has-text("Devices"), :has-text("Sites"), :has-text("Alerts")').first(); if (await category.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(category).toBeVisible(); } } }); test('can navigate to result from global search', async ({ page }) => { await page.goto('/dashboard'); const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('test'); await page.waitForTimeout(1000); // Click first result const firstResult = page.locator('[data-search-result], [role="option"]').first(); if (await firstResult.isVisible({ timeout: 2000 }).catch(() => false)) { await firstResult.click(); await page.waitForTimeout(500); // Should navigate to detail page await expect(page.locator('body')).toBeVisible(); } } }); test('search supports keyboard navigation', async ({ page }) => { await page.goto('/dashboard'); const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('test'); await page.waitForTimeout(1000); // Press down arrow to navigate results await searchInput.press('ArrowDown'); await page.waitForTimeout(200); // Press enter to select await searchInput.press('Enter'); await page.waitForTimeout(500); // Should navigate await expect(page.locator('body')).toBeVisible(); } }); test('search shows empty state for no results', async ({ page }) => { await page.goto('/dashboard'); const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('xyznonexistent12345'); await page.waitForTimeout(1000); // Should show no results message const noResults = page.locator(':has-text("No results"), :has-text("not found")').first(); if (await noResults.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(noResults).toBeVisible(); } } }); }); test.describe('List Filtering', () => { test('can filter devices by status', async ({ page }) => { await page.goto('/devices'); const statusFilter = page.locator('button:has-text("Online"), button:has-text("Offline")').first(); if (await statusFilter.isVisible({ timeout: 2000 }).catch(() => false)) { await statusFilter.click(); await page.waitForTimeout(500); // URL should update const url = page.url(); if (url.includes('status=')) { expect(url).toContain('status='); } } }); test('can filter alerts by severity', async ({ page }) => { await page.goto('/alerts'); const severityFilter = page.locator('button:has-text("Critical"), button:has-text("Warning")').first(); if (await severityFilter.isVisible({ timeout: 2000 }).catch(() => false)) { await severityFilter.click(); await page.waitForTimeout(500); } }); test('can combine multiple filters', async ({ page }) => { await page.goto('/devices'); // Apply status filter const statusFilter = page.locator('button:has-text("Online")').first(); if (await statusFilter.isVisible({ timeout: 2000 }).catch(() => false)) { await statusFilter.click(); await page.waitForTimeout(500); // Apply additional filter const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible()) { await searchInput.fill('router'); await page.waitForTimeout(500); // URL should have multiple params const url = page.url(); if (url.includes('status=') || url.includes('search=')) { expect(url.length).toBeGreaterThan(0); } } } }); test('shows active filter count', async ({ page }) => { await page.goto('/devices'); const statusFilter = page.locator('button:has-text("Online")').first(); if (await statusFilter.isVisible({ timeout: 2000 }).catch(() => false)) { await statusFilter.click(); await page.waitForTimeout(500); // Look for active filter indicator const filterCount = page.locator('[data-filter-count], .badge, :has-text("filter")').first(); if (await filterCount.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(filterCount).toBeVisible(); } } }); test('can clear individual filter', async ({ page }) => { await page.goto('/devices?status=online'); const clearButton = page.locator('button:has-text("Clear"), button:has-text("×")').first(); if (await clearButton.isVisible({ timeout: 2000 }).catch(() => false)) { await clearButton.click(); await page.waitForTimeout(500); // URL should update const url = page.url(); expect(url).not.toContain('status=online'); } }); test('can clear all filters', async ({ page }) => { await page.goto('/devices?status=online&sort=name'); const clearAllButton = page.locator('button:has-text("Clear all"), button:has-text("Reset")').first(); if (await clearAllButton.isVisible({ timeout: 2000 }).catch(() => false)) { await clearAllButton.click(); await page.waitForTimeout(500); // URL should be clean const url = page.url(); expect(url).toBe(page.url().split('?')[0]); } }); }); test.describe('Advanced Search Features', () => { test('supports wildcard search', async ({ page }) => { await page.goto('/devices'); const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('router*'); await page.waitForTimeout(500); // Should search with wildcard await expect(page.locator('body')).toBeVisible(); } }); test('search is case-insensitive', async ({ page }) => { await page.goto('/devices'); const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('TEST'); await page.waitForTimeout(500); // Should find results regardless of case await expect(page.locator('body')).toBeVisible(); } }); test('can search by IP address', async ({ page }) => { await page.goto('/devices'); const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('192.168'); await page.waitForTimeout(500); // Should search IPs await expect(page.locator('body')).toBeVisible(); } }); test('can search by MAC address', async ({ page }) => { await page.goto('/devices'); const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('00:11:22'); await page.waitForTimeout(500); // Should search MACs await expect(page.locator('body')).toBeVisible(); } }); test('search highlights matching terms', async ({ page }) => { await page.goto('/devices'); const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('router'); await page.waitForTimeout(500); // Look for highlighted text const highlighted = page.locator('mark, .highlight, strong').first(); if (await highlighted.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(highlighted).toBeVisible(); } } }); }); test.describe('Filter Presets', () => { test('can save filter preset', async ({ page }) => { await page.goto('/devices?status=online&sort=name'); const savePresetButton = page.locator('button:has-text("Save filter"), button:has-text("Save preset")').first(); if (await savePresetButton.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(savePresetButton).toBeVisible(); } }); test('can load saved filter preset', async ({ page }) => { await page.goto('/devices'); const presetButton = page.locator('button:has-text("Preset"), [data-preset]').first(); if (await presetButton.isVisible({ timeout: 2000 }).catch(() => false)) { await presetButton.click(); await page.waitForTimeout(500); // Filters should be applied await expect(page.locator('body')).toBeVisible(); } }); }); test.describe('Date Range Filtering', () => { test('can filter by date range', async ({ page }) => { await page.goto('/alerts'); const dateFilter = page.locator('input[type="date"]').first(); if (await dateFilter.isVisible({ timeout: 2000 }).catch(() => false)) { await dateFilter.fill('2024-01-01'); await page.waitForTimeout(500); // Should filter by date await expect(page.locator('body')).toBeVisible(); } }); test('shows relative date options', async ({ page }) => { await page.goto('/alerts'); const relativeDateButton = page.locator('button:has-text("Last 24h"), button:has-text("Last 7d")').first(); if (await relativeDateButton.isVisible({ timeout: 2000 }).catch(() => false)) { await relativeDateButton.click(); await page.waitForTimeout(500); } }); test('can use custom date range', async ({ page }) => { await page.goto('/alerts'); const customRangeButton = page.locator('button:has-text("Custom"), button:has-text("Date range")').first(); if (await customRangeButton.isVisible({ timeout: 2000 }).catch(() => false)) { await customRangeButton.click(); await page.waitForTimeout(300); // Date picker should appear const datePicker = page.locator('[role="dialog"], .date-picker').first(); if (await datePicker.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(datePicker).toBeVisible(); } } }); }); test.describe('Export Filtered Results', () => { test('can export filtered results', async ({ page }) => { await page.goto('/devices?status=online'); const exportButton = page.locator('button:has-text("Export"), button:has-text("Download")').first(); if (await exportButton.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(exportButton).toBeVisible(); } }); test('export respects current filters', async ({ page }) => { await page.goto('/devices?status=online'); const exportButton = page.locator('button:has-text("Export")').first(); if (await exportButton.isVisible({ timeout: 2000 }).catch(() => false)) { await exportButton.click(); await page.waitForTimeout(300); // Export dialog or download should start await expect(page.locator('body')).toBeVisible(); } }); }); test.describe('Result Count Display', () => { test('shows result count after filtering', async ({ page }) => { await page.goto('/devices'); const statusFilter = page.locator('button:has-text("Online")').first(); if (await statusFilter.isVisible({ timeout: 2000 }).catch(() => false)) { await statusFilter.click(); await page.waitForTimeout(500); // Look for result count const resultCount = page.locator(':has-text("result"), :has-text("device"), .count').first(); if (await resultCount.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(resultCount).toBeVisible(); } } }); test('shows "no results" when filter returns empty', async ({ page }) => { await page.goto('/devices'); const searchInput = page.locator('input[type="search"]').first(); if (await searchInput.isVisible({ timeout: 2000 }).catch(() => false)) { await searchInput.fill('xyznonexistent12345'); await page.waitForTimeout(500); const noResults = page.locator(':has-text("No"), :has-text("results"), .empty-state').first(); if (await noResults.isVisible({ timeout: 2000 }).catch(() => false)) { await expect(noResults).toBeVisible(); } } }); }); });