91 lines
9.1 KiB
TypeScript
91 lines
9.1 KiB
TypeScript
import { test, expect, type Page } from '@playwright/test'
|
|
|
|
async function fixture(page: Page, options: { fail?: boolean; pending?: boolean; slow?: boolean } = {}) {
|
|
const queries: URLSearchParams[] = [], exports: URLSearchParams[] = [], writes: string[] = [], now = new Date().toISOString()
|
|
await page.addInitScript(() => sessionStorage.setItem('vdi.accessToken', 'task-history-fixture'))
|
|
const task = (i: number, extra = {}) => ({ id: `task-${i}`, kind: 'desktop', action: 'start', targetId: `desk-${i}`, targetName: `历史桌面 ${i}`, actor: 'root', status: 'success', message: '真实回执已保存', providerRef: '', createdAt: now, updatedAt: now, ...extra })
|
|
await page.route('**/api/v1/**', async route => {
|
|
const request = route.request(), url = new URL(request.url()), path = url.pathname.replace('/api/v1', '')
|
|
const list = (items: unknown[], extra = {}) => route.fulfill({ json: { items, total: items.length, page: 1, pageSize: 100, requestId: 'task-ui', ...extra } })
|
|
if (request.method() !== 'GET') { writes.push(path); await route.fulfill({ status: 403, json: { message: '不应发送业务写请求' } }); return }
|
|
if (path === '/auth/me') { await route.fulfill({ json: { id: 'reader', username: 'reader', displayName: '任务查看员', role: options.pending ? 'operator' : 'viewer' } }); return }
|
|
if (path === '/settings') { await route.fulfill({ json: { mode: 'production', capabilities: ['task-history'], pveConfigured: false } }); return }
|
|
if (path === '/overview') { await route.fulfill({ json: { terminalCount: 0, onlineTerminals: 0, desktopCount: options.pending ? 1 : 0, runningDesktops: 0, activeSessions: 0, failedTasks: 0, mode: 'production' } }); return }
|
|
if (path === '/tasks/export') { exports.push(url.searchParams); await route.fulfill({ contentType: 'text/csv; charset=utf-8', body: '\ufeff任务标识,状态\r\ntask-1,success\r\n' }); return }
|
|
if (path === '/tasks') {
|
|
queries.push(url.searchParams)
|
|
const p = Number(url.searchParams.get('page') || 1), size = Number(url.searchParams.get('pageSize') || 20), active = url.searchParams.get('active') === 'true'
|
|
if (active) {
|
|
const total = options.pending ? 101 : 0
|
|
await list(Array.from({ length: Math.max(0, Math.min(size, total - (p - 1) * size)) }, (_, i) => task(2000 + (p - 1) * size + i, { status: 'queued', targetId: (p - 1) * size + i === 100 ? 'desk-pending' : `pending-${i}` })), { total, page: p, pageSize: size }); return
|
|
}
|
|
const keyword = url.searchParams.get('keyword') || ''
|
|
if (options.fail && keyword) { await route.fulfill({ status: 500, json: { code: 'database_error', message: '任务查询暂不可用', requestId: 'task-ui' } }); return }
|
|
if (options.slow && keyword === 'slow') await new Promise(resolve => setTimeout(resolve, 700))
|
|
const filtered = !!url.searchParams.get('actor') || !!keyword, total = options.pending ? 1500 : filtered ? 1 : 45
|
|
await list(Array.from({ length: Math.max(0, Math.min(size, total - (p - 1) * size)) }, (_, i) => task((p - 1) * size + i + 1, filtered ? { targetName: keyword || '筛选桌面', actor: url.searchParams.get('actor') || 'root' } : {})), { total, page: p, pageSize: size }); return
|
|
}
|
|
if (path === '/desktops' && options.pending) { await list([{ id: 'desk-pending', name: '有旧任务的桌面', vmid: 9000, node: 'test', os: 'Ubuntu', pool: '', provider: 'proxmox', powerState: 'stopped', serviceState: 'unknown', setupRequired: false, endpoint: '192.0.2.1', width: 1920, height: 1080, fps: 30, updatedAt: now }]); return }
|
|
if (['/resources', '/desktops', '/users', '/terminals', '/audit-logs'].includes(path)) { await list([]); return }
|
|
await route.fulfill({ status: 404, json: { message: '未定义的测试接口' } })
|
|
})
|
|
return { queries, exports, writes }
|
|
}
|
|
|
|
test('任务历史分页、详情和深浅窄屏,历史读取不依赖全局近期任务', async ({ page }, info) => {
|
|
const calls = await fixture(page); await page.goto('/tasks')
|
|
await expect(page.getByRole('button', { name: '历史桌面 1', exact: true })).toBeVisible()
|
|
const initialPages = calls.queries.filter(q => q.get('pageSize') === '20')
|
|
const cutoffBeforePaging = initialPages[initialPages.length - 1]?.get('to')
|
|
await page.locator('.el-pagination .btn-next').click(); await expect(page.getByRole('button', { name: '历史桌面 21', exact: true })).toBeVisible()
|
|
await page.getByRole('button', { name: '历史桌面 21', exact: true }).click()
|
|
const drawer = page.getByRole('dialog', { name: '任务详情' })
|
|
await expect(drawer.getByText('task-21', { exact: true })).toBeVisible()
|
|
await expect(drawer.getByRole('button', { name: /核对/ })).toHaveCount(0)
|
|
await drawer.locator('.el-drawer__close-btn').click()
|
|
await page.screenshot({ path: info.outputPath('task-history-dark.png'), fullPage: true, animations: 'disabled' })
|
|
await page.getByRole('button', { name: '切换浅色主题' }).click(); await page.setViewportSize({ width: 390, height: 844 })
|
|
await page.screenshot({ path: info.outputPath('task-history-mobile.png'), fullPage: true, animations: 'disabled' })
|
|
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(391)
|
|
const pages = calls.queries.filter(q => q.get('pageSize') === '20')
|
|
expect(pages[pages.length - 1]?.get('page')).toBe('2'); expect(pages[pages.length - 1]?.get('to')).toBe(cutoffBeforePaging)
|
|
expect(calls.writes).toEqual([])
|
|
})
|
|
|
|
test('任务筛选和导出沿用已应用条件,修改草稿后导出不混用', async ({ page }) => {
|
|
const calls = await fixture(page); await page.goto('/tasks'); await expect(page.getByRole('button', { name: '历史桌面 1', exact: true })).toBeVisible()
|
|
await page.getByRole('textbox', { name: '任务操作者' }).fill('root_%')
|
|
await page.locator('.el-select').filter({ has: page.getByRole('combobox', { name: '任务状态筛选' }) }).click(); await page.getByRole('option', { name: '成功', exact: true }).click()
|
|
await page.getByRole('combobox', { name: '任务动作' }).click(); await page.getByRole('option', { name: '开机', exact: true }).click()
|
|
await page.getByRole('combobox', { name: '任务类型' }).click(); await page.getByRole('option', { name: '桌面电源', exact: true }).click()
|
|
await page.getByRole('combobox', { name: '任务时间范围' }).first().click(); await page.getByRole('button', { name: '最近 7 天', exact: true }).click()
|
|
await page.getByRole('button', { name: '查询任务', exact: true }).click(); await expect(page.getByRole('button', { name: '筛选桌面', exact: true })).toBeVisible()
|
|
await page.getByRole('textbox', { name: '搜索任务' }).fill('尚未应用')
|
|
const wait = page.waitForEvent('download'); await page.getByRole('button', { name: '导出任务 CSV' }).click()
|
|
expect((await wait).suggestedFilename()).toMatch(/执行任务.*\.csv$/)
|
|
expect(Object.fromEntries(calls.exports[0]!)).toMatchObject({ actor: 'root_%', status: 'success', action: 'start', kind: 'desktop', keyword: '' })
|
|
expect(calls.exports[0]?.get('from')).toBeTruthy(); expect(calls.exports[0]?.get('page')).toBeNull()
|
|
await page.getByRole('button', { name: '重置', exact: true }).click(); await expect(page.getByRole('button', { name: '历史桌面 1', exact: true })).toBeVisible()
|
|
expect(calls.writes).toEqual([])
|
|
})
|
|
|
|
test('查询失败清理旧结果并禁用导出,旧响应不能覆盖新条件', async ({ page }) => {
|
|
await fixture(page, { fail: true }); await page.goto('/tasks'); await expect(page.getByRole('button', { name: '历史桌面 1', exact: true })).toBeVisible()
|
|
await page.getByRole('textbox', { name: '搜索任务' }).fill('failure'); await page.getByRole('button', { name: '查询任务', exact: true }).click()
|
|
await expect(page.getByText(/任务查询暂不可用/)).toBeVisible(); await expect(page.getByRole('button', { name: '导出任务 CSV' })).toBeDisabled()
|
|
await expect(page.getByRole('button', { name: '历史桌面 1', exact: true })).toHaveCount(0)
|
|
await page.unrouteAll(); await fixture(page, { slow: true }); await page.reload(); await expect(page.getByRole('button', { name: '历史桌面 1', exact: true })).toBeVisible()
|
|
await page.getByRole('textbox', { name: '搜索任务' }).fill('slow'); await page.getByRole('textbox', { name: '搜索任务' }).press('Enter')
|
|
await page.getByRole('textbox', { name: '搜索任务' }).fill('new'); await page.getByRole('textbox', { name: '搜索任务' }).press('Enter')
|
|
await expect(page.getByRole('button', { name: 'new', exact: true })).toBeVisible()
|
|
await expect.poll(async () => { await page.waitForTimeout(800); return page.getByRole('button', { name: 'slow', exact: true }).count() }).toBe(0)
|
|
})
|
|
|
|
test('全局只取近期一页并分页拉全旧活动任务,旧任务仍阻止操作桌面', async ({ page }) => {
|
|
const calls = await fixture(page, { pending: true }); await page.goto('/desktops')
|
|
await expect(page.getByRole('button', { name: '任务执行中', exact: true })).toBeDisabled()
|
|
expect(calls.queries.some(q => q.get('active') === 'true' && q.get('page') === '2')).toBe(true)
|
|
expect(calls.queries.some(q => q.get('active') !== 'true' && q.get('page') === '2')).toBe(false)
|
|
expect(calls.writes).toEqual([])
|
|
})
|