first commit

This commit is contained in:
Beyhan Oğur
2026-04-26 21:52:23 +03:00
commit 880f412e2c
2662 changed files with 866266 additions and 0 deletions

View File

@@ -0,0 +1,427 @@
import { expect, test } from '../../core/fixtures/base.fixture'
import { createLogSearchQuery, SAMPLE_MODELS, SAMPLE_PROVIDERS } from './logs.data'
test.describe('LLM Logs', () => {
test.beforeEach(async ({ logsPage }) => {
await logsPage.goto()
})
test.describe('Logs Display', () => {
test('should display logs table', async ({ logsPage }) => {
// Table should be visible after goto (which waits for load)
const tableExists = await logsPage.logsTable.isVisible().catch(() => false)
expect(tableExists).toBe(true)
})
test('should display stats cards', async ({ logsPage }) => {
const statsVisible = await logsPage.areStatsVisible()
expect(statsVisible).toBe(true)
})
test('should display filters section', async ({ logsPage }) => {
// Check if the search input or filters button is visible
// These are always visible when the page loads (not inside empty state)
const searchVisible = await logsPage.searchInput.isVisible().catch(() => false)
const filtersButtonVisible = await logsPage.filtersButton.isVisible().catch(() => false)
// Either search input OR filters button should be visible
expect(searchVisible || filtersButtonVisible).toBe(true)
})
})
test.describe('Log Filtering', () => {
test('should filter logs by provider', async ({ logsPage }) => {
// Try to filter by first available provider
const providerFilter = logsPage.providerFilter
const isVisible = await providerFilter.isVisible().catch(() => false)
if (!isVisible || SAMPLE_PROVIDERS.length === 0) {
test.skip(!isVisible || SAMPLE_PROVIDERS.length === 0, 'Provider filter not visible or no sample providers')
return
}
// Get initial filter state
const initialValue = await providerFilter.textContent().catch(() => '')
await logsPage.filterByProvider(SAMPLE_PROVIDERS[0])
// Check that filter value changed (or verify filter is applied via DOM)
const newValue = await providerFilter.textContent().catch(() => '')
// Filter should have changed or show selected provider
expect(newValue || initialValue).toBeTruthy()
})
test('should filter logs by model', async ({ logsPage }) => {
const modelFilter = logsPage.modelFilter
const isVisible = await modelFilter.isVisible().catch(() => false)
if (!isVisible || SAMPLE_MODELS.length === 0) {
test.skip(!isVisible || SAMPLE_MODELS.length === 0, 'Model filter not visible or no sample models')
return
}
// Get initial filter state
const initialValue = await modelFilter.textContent().catch(() => '')
await logsPage.filterByModel(SAMPLE_MODELS[0])
// Check that filter value changed (or verify filter is applied via DOM)
const newValue = await modelFilter.textContent().catch(() => '')
// Filter should have changed or show selected model
expect(newValue || initialValue).toBeTruthy()
})
test('should filter logs by status', async ({ logsPage, page }) => {
const filtersVisible = await logsPage.filtersButton.isVisible().catch(() => false)
if (!filtersVisible) {
test.skip(true, 'Filters button not visible')
return
}
await logsPage.filterByStatus('success')
// Assert status filter is applied: logs page persists filters in URL (e.g. status=success)
await expect
.poll(() => page.url(), { timeout: 5000, intervals: [200, 300, 500] })
.toMatch(/status=success/)
})
test('should search logs by content', async ({ logsPage }) => {
const searchInput = logsPage.searchInput
const isVisible = await searchInput.isVisible().catch(() => false)
if (!isVisible) {
test.skip(true, 'Search input not visible')
return
}
const query = createLogSearchQuery()
await logsPage.searchLogs(query)
// Check that search input contains the query (DOM state)
const inputValue = await searchInput.inputValue().catch(() => '')
expect(inputValue).toContain(query)
})
test('should clear search', async ({ logsPage }) => {
const searchInput = logsPage.searchInput
const isVisible = await searchInput.isVisible().catch(() => false)
if (!isVisible) {
test.skip(true, 'Search input not visible')
return
}
await logsPage.searchLogs('test query')
await logsPage.clearSearch()
// Search should be cleared
const inputValue = await searchInput.inputValue().catch(() => '')
expect(inputValue).toBe('')
})
test('should filter by time period', async ({ logsPage }) => {
const datePicker = logsPage.dateRangePicker
const isVisible = await datePicker.isVisible().catch(() => false)
if (!isVisible) {
test.skip(true, 'Date range picker not visible')
return
}
// Get initial date picker value
const initialValue = await datePicker.textContent().catch(() => '')
await logsPage.selectTimePeriod('7d')
// Check that date picker value changed (DOM state)
const newValue = await datePicker.textContent().catch(() => '')
// Date picker should show "Last 7 days" or similar
expect(newValue || initialValue).toBeTruthy()
})
})
test.describe('Log Details', () => {
test('should open log details sheet', async ({ logsPage }) => {
// Wait a bit for logs to potentially load
await logsPage.page.waitForTimeout(1000)
const logCount = await logsPage.getLogCount()
if (logCount > 0) {
await logsPage.viewLogDetails(0)
// Wait for sheet animation
await logsPage.page.waitForTimeout(500)
// Detail sheet should be visible
const sheetVisible = await logsPage.logDetailSheet.isVisible().catch(() => false)
expect(sheetVisible).toBe(true)
// Close the sheet
await logsPage.closeLogDetails()
} else {
// If no logs exist, the test passes (nothing to click)
expect(logCount).toBe(0)
}
})
test('should close log details sheet', async ({ logsPage }) => {
const logCount = await logsPage.getLogCount()
if (logCount > 0) {
await logsPage.viewLogDetails(0)
await logsPage.closeLogDetails()
// Sheet should be closed
const sheetVisible = await logsPage.logDetailSheet.isVisible().catch(() => false)
expect(sheetVisible).toBe(false)
}
})
})
test.describe('Pagination', () => {
test('should navigate to next page', async ({ logsPage }) => {
// Wait for pagination to settle (useTablePageSize may adjust limit dynamically)
await logsPage.page.waitForTimeout(2000)
const paginationVisible = await logsPage.paginationControls.isVisible().catch(() => false)
if (!paginationVisible) {
test.skip(true, 'Pagination controls not visible')
return
}
const nextBtn = logsPage.nextPageBtn.first()
const isEnabled = await nextBtn.isEnabled().catch(() => false)
if (!isEnabled) {
test.skip(true, 'Only one page of results; skipping pagination test')
return
}
const initialPage = logsPage.getCurrentPageNumber()
expect(initialPage).toBe(1)
await logsPage.goToNextPage()
await expect
.poll(() => logsPage.getCurrentPageNumber(), { timeout: 5000 })
.toBe(initialPage + 1)
})
test('should navigate to previous page', async ({ logsPage }) => {
// Wait for pagination to settle (useTablePageSize may adjust limit dynamically)
await logsPage.page.waitForTimeout(2000)
const paginationVisible = await logsPage.paginationControls.isVisible().catch(() => false)
if (!paginationVisible) {
test.skip(true, 'Pagination controls not visible')
return
}
const nextBtn = logsPage.nextPageBtn.first()
const nextEnabled = await nextBtn.isEnabled().catch(() => false)
if (!nextEnabled) {
test.skip(true, 'Only one page of results; skipping pagination test')
return
}
await logsPage.goToNextPage()
await expect
.poll(() => logsPage.getCurrentPageNumber(), { timeout: 5000 })
.toBe(2)
const prevBtn = logsPage.prevPageBtn.first()
const prevEnabled = await prevBtn.isEnabled().catch(() => false)
if (!prevEnabled) {
test.skip(true, 'Only one page of results; skipping previous-page test')
return
}
await logsPage.goToPreviousPage()
await expect
.poll(() => logsPage.getCurrentPageNumber(), { timeout: 5000 })
.toBe(1)
})
})
test.describe('Table Sorting', () => {
test('should sort by timestamp', async ({ logsPage }) => {
// Timestamp is the default sort column (desc), so clicking it toggles to asc
await logsPage.sortBy('timestamp')
// Timestamp sort toggles order; wait for URL to reflect the change
await logsPage.page.waitForURL(/order=asc|sort_by=timestamp/, { timeout: 5000 })
})
test('should sort by latency', async ({ logsPage }) => {
await logsPage.sortBy('latency')
// Wait for URL to update
await logsPage.page.waitForURL(/sort_by=latency/, { timeout: 5000 })
// Check URL state for latency sort
const sortState = await logsPage.getSortState('latency')
expect(sortState).toBeTruthy()
})
test('should sort by cost', async ({ logsPage }) => {
await logsPage.sortBy('cost')
// Wait for URL to update
await logsPage.page.waitForURL(/sort_by=cost/, { timeout: 5000 })
// Check URL state for cost sort
const sortState = await logsPage.getSortState('cost')
expect(sortState).toBeTruthy()
})
})
test.describe('Live Updates', () => {
test('should toggle live updates', async ({ logsPage }) => {
const liveToggle = logsPage.liveToggle
const isVisible = await liveToggle.isVisible().catch(() => false)
if (!isVisible) {
test.skip(true, 'Live toggle not visible')
return
}
// Default is live_enabled=true (but URL may not have it since it's the default)
// Check for live_enabled=false to determine if currently disabled
const initialUrl = logsPage.page.url()
const initialLiveDisabled = initialUrl.includes('live_enabled=false')
await logsPage.toggleLiveUpdates()
// Wait for URL to reflect live_enabled toggle
await logsPage.page.waitForURL(/live_enabled=/, { timeout: 5000 })
const newUrl = logsPage.page.url()
const newLiveDisabled = newUrl.includes('live_enabled=false')
// Live enabled state should have toggled
// If initially enabled (not disabled), after toggle it should be disabled
// If initially disabled, after toggle it should be enabled (no live_enabled=false)
expect(newLiveDisabled).not.toBe(initialLiveDisabled)
})
})
test.describe('Empty State', () => {
test('should show empty state when no logs', async ({ logsPage }) => {
// Try to filter by a non-existent provider
const searchInput = logsPage.searchInput
const isVisible = await searchInput.isVisible().catch(() => false)
if (!isVisible) {
test.skip(true, 'Search input not visible')
return
}
await logsPage.searchLogs(`nonexistent-query-${Date.now()}`)
// After searching for a non-existent query, empty state should appear (wait for API + render)
await expect(
logsPage.page.locator('text=/No results found|No logs found/i')
).toBeVisible({ timeout: 10000 })
})
})
test.describe('Advanced Filtering', () => {
test('should combine multiple filters', async ({ logsPage }) => {
// Apply multiple filters if they're visible
const searchVisible = await logsPage.searchInput.isVisible().catch(() => false)
const providerVisible = await logsPage.providerFilter.isVisible().catch(() => false)
if (!searchVisible || !providerVisible) {
test.skip(true, 'Search input or provider filter not visible')
return
}
// Apply search filter
await logsPage.searchLogs('test')
// Apply provider filter
if (SAMPLE_PROVIDERS.length > 0) {
await logsPage.filterByProvider(SAMPLE_PROVIDERS[0])
}
// Both filters should be applied
const searchValue = await logsPage.searchInput.inputValue().catch(() => '')
expect(searchValue).toContain('test')
})
test('should clear all filters', async ({ logsPage }) => {
const searchVisible = await logsPage.searchInput.isVisible().catch(() => false)
if (!searchVisible) {
test.skip(true, 'Search input not visible')
return
}
// Apply a filter first
await logsPage.searchLogs('test query to clear')
// Clear the search
await logsPage.clearSearch()
// Search should be empty
const searchValue = await logsPage.searchInput.inputValue().catch(() => '')
expect(searchValue).toBe('')
})
test('should search within filtered results', async ({ logsPage }) => {
const searchVisible = await logsPage.searchInput.isVisible().catch(() => false)
const statusVisible = await logsPage.statusFilter.isVisible().catch(() => false)
if (!searchVisible || !statusVisible) {
test.skip(true, 'Search input or status filter not visible')
return
}
// Apply status filter first
await logsPage.filterByStatus('success')
// Then apply search
await logsPage.searchLogs('api')
// Search input should contain the query
const searchValue = await logsPage.searchInput.inputValue().catch(() => '')
expect(searchValue).toContain('api')
})
})
test.describe('URL State Persistence', () => {
test('should persist filters in URL', async ({ logsPage }) => {
const searchVisible = await logsPage.searchInput.isVisible().catch(() => false)
if (!searchVisible) return
await logsPage.searchLogs('persistent-search')
// Search is debounced (500ms) then URL updates; wait for URL to contain the param
await expect
.poll(
() => logsPage.page.url(),
{ timeout: 8000, intervals: [300, 500, 500] }
)
.toContain('content_search=')
const url = logsPage.page.url()
// Value may be percent-encoded (e.g. persistent-search → persistent%2Dsearch)
expect(decodeURIComponent(url)).toContain('persistent-search')
})
test('should restore state from URL', async ({ logsPage, page }) => {
// Logs page uses start_time and end_time (unix timestamps), not period
const endTime = Math.floor(Date.now() / 1000)
const startTime = endTime - 7 * 24 * 60 * 60 // 7 days ago
await page.goto(`/workspace/logs?start_time=${startTime}&end_time=${endTime}`)
// Wait for page to load and URL to reflect state (nuqs may merge or keep params)
await expect
.poll(() => page.url(), { timeout: 5000, intervals: [200, 300, 500] })
.toMatch(/start_time=\d+/)
const url = page.url()
expect(url).toMatch(/start_time=\d+/)
expect(url).toMatch(/end_time=\d+/)
})
})
})