import { test, expect } from '@playwright/test'; test.describe('Search Functionality', () => { test.beforeEach(async ({ page }) => { // Navigate to the app await page.goto('/'); // Wait for the app to load await page.waitForLoadState('networkidle'); }); test('should display search panel', async ({ page }) => { // Open search panel (adjust selector based on your UI) const searchPanel = page.locator('app-search-panel'); await expect(searchPanel).toBeVisible(); }); test('should perform basic content search', async ({ page }) => { // Find search input const searchInput = page.locator('input[type="text"]').first(); // Type search query await searchInput.fill('content:test'); await searchInput.press('Enter'); // Wait for results await page.waitForSelector('.search-results', { timeout: 5000 }); // Verify results are displayed const resultsCount = page.locator('text=/\\d+ results?/'); await expect(resultsCount).toBeVisible(); }); test('should filter by file name', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('file:readme.md'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Results should only contain readme.md files const fileNames = page.locator('.file-name'); const count = await fileNames.count(); if (count > 0) { for (let i = 0; i < count; i++) { const text = await fileNames.nth(i).textContent(); expect(text?.toLowerCase()).toContain('readme'); } } }); test('should filter by path', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('path:"Daily notes"'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Results should only contain files from Daily notes path const filePaths = page.locator('.file-path'); const count = await filePaths.count(); if (count > 0) { const text = await filePaths.first().textContent(); expect(text?.toLowerCase()).toContain('daily'); } }); test('should filter by tag', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('tag:#work'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Should show results with #work tag const results = page.locator('.search-result'); await expect(results.first()).toBeVisible({ timeout: 5000 }); }); test('should toggle case sensitivity', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); const caseButton = page.locator('button:has-text("Aa")'); // Search with case insensitive (default) await searchInput.fill('TEST'); await searchInput.press('Enter'); await page.waitForTimeout(500); const resultsInsensitive = page.locator('text=/\\d+ results?/'); const insensitiveText = await resultsInsensitive.textContent(); // Clear and toggle case sensitivity await searchInput.clear(); await caseButton.click(); // Search with case sensitive await searchInput.fill('TEST'); await searchInput.press('Enter'); await page.waitForTimeout(500); const resultsSensitive = page.locator('text=/\\d+ results?/'); const sensitiveText = await resultsSensitive.textContent(); // Results should be different (assuming there are lowercase 'test' matches) // This test assumes the vault has both 'test' and 'TEST' }); test('should toggle collapse results', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); // Perform search await searchInput.fill('content:test'); await searchInput.press('Enter'); // Wait for results await page.waitForSelector('text=/\\d+ results?/', { timeout: 5000 }); // Find collapse toggle const collapseToggle = page.locator('text=Collapse results').locator('..').locator('input[type="checkbox"]'); if (await collapseToggle.isVisible()) { // Toggle collapse await collapseToggle.click(); await page.waitForTimeout(300); // Verify results are collapsed const expandedGroups = page.locator('.result-group.expanded'); const count = await expandedGroups.count(); expect(count).toBe(0); } }); test('should toggle show more context', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); // Perform search await searchInput.fill('content:test'); await searchInput.press('Enter'); // Wait for results await page.waitForSelector('text=/\\d+ results?/', { timeout: 5000 }); // Find show more context toggle const contextToggle = page.locator('text=Show more context').locator('..').locator('input[type="checkbox"]'); if (await contextToggle.isVisible()) { // Get initial context length const matchContext = page.locator('.match-context').first(); const initialText = await matchContext.textContent(); const initialLength = initialText?.length || 0; // Toggle show more context await contextToggle.click(); await page.waitForTimeout(1000); // Wait for re-search // Get new context length const newText = await matchContext.textContent(); const newLength = newText?.length || 0; // Context should be longer (more lines) expect(newLength).toBeGreaterThanOrEqual(initialLength); } }); test('should highlight matches in results', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('test'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Check for highlighted text const highlights = page.locator('mark'); const count = await highlights.count(); expect(count).toBeGreaterThan(0); // Verify highlight contains search term if (count > 0) { const highlightText = await highlights.first().textContent(); expect(highlightText?.toLowerCase()).toContain('test'); } }); test('should handle complex queries with AND operator', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('test AND example'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Results should contain both terms const results = page.locator('.search-result'); if (await results.count() > 0) { const firstResult = await results.first().textContent(); expect(firstResult?.toLowerCase()).toContain('test'); expect(firstResult?.toLowerCase()).toContain('example'); } }); test('should handle complex queries with OR operator', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('test OR example'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Results should contain at least one term const results = page.locator('.search-result'); await expect(results.first()).toBeVisible({ timeout: 5000 }); }); test('should handle negation operator', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('test -deprecated'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Results should not contain 'deprecated' const results = page.locator('.search-result'); const count = await results.count(); if (count > 0) { for (let i = 0; i < Math.min(count, 5); i++) { const text = await results.nth(i).textContent(); expect(text?.toLowerCase()).not.toContain('deprecated'); } } }); test('should handle regex search', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); const regexButton = page.locator('button:has-text(".*")'); // Enable regex mode await regexButton.click(); // Search with regex pattern await searchInput.fill('test\\d+'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Should find matches like test1, test2, etc. const results = page.locator('.search-result'); if (await results.count() > 0) { await expect(results.first()).toBeVisible(); } }); test('should handle property search', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('[status]:draft'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Should find notes with status: draft const results = page.locator('.search-result'); if (await results.count() > 0) { await expect(results.first()).toBeVisible(); } }); test('should handle task search', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('task-todo:review'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Should find incomplete tasks containing 'review' const results = page.locator('.search-result'); if (await results.count() > 0) { await expect(results.first()).toBeVisible(); } }); test('should expand and collapse result groups', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('test'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Find first result group const firstGroup = page.locator('.result-group').first(); const expandButton = firstGroup.locator('.expand-button, .collapse-button, svg').first(); if (await expandButton.isVisible()) { // Click to collapse await expandButton.click(); await page.waitForTimeout(300); // Matches should be hidden const matches = firstGroup.locator('.match-item'); await expect(matches.first()).not.toBeVisible(); // Click to expand await expandButton.click(); await page.waitForTimeout(300); // Matches should be visible await expect(matches.first()).toBeVisible(); } }); test('should sort results by different criteria', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); await searchInput.fill('test'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Find sort dropdown const sortSelect = page.locator('select').filter({ hasText: /Relevance|Name|Modified/ }); if (await sortSelect.isVisible()) { // Sort by name await sortSelect.selectOption('name'); await page.waitForTimeout(300); // Verify sorting (check first two results are alphabetically ordered) const fileNames = page.locator('.file-name'); if (await fileNames.count() >= 2) { const first = await fileNames.nth(0).textContent(); const second = await fileNames.nth(1).textContent(); expect(first?.localeCompare(second || '') || 0).toBeLessThanOrEqual(0); } } }); test('should persist search preferences', async ({ page }) => { const searchInput = page.locator('input[type="text"]').first(); // Perform search and toggle collapse await searchInput.fill('test'); await searchInput.press('Enter'); await page.waitForTimeout(1000); const collapseToggle = page.locator('text=Collapse results').locator('..').locator('input[type="checkbox"]'); if (await collapseToggle.isVisible()) { await collapseToggle.click(); await page.waitForTimeout(300); // Reload page await page.reload(); await page.waitForLoadState('networkidle'); // Perform search again await searchInput.fill('test'); await searchInput.press('Enter'); await page.waitForTimeout(1000); // Collapse preference should be persisted const isChecked = await collapseToggle.isChecked(); expect(isChecked).toBe(true); } }); });