90 lines
7.1 KiB
TypeScript
90 lines
7.1 KiB
TypeScript
import { test, expect, type Page } from '@playwright/test'
|
|
|
|
async function fixture(page: Page, options: { fail?: boolean; deny?: boolean; slow?: boolean } = {}) {
|
|
const revoked: string[] = [], queries: string[] = []
|
|
const now = new Date().toISOString(), expires = new Date(Date.now() + 3600000).toISOString()
|
|
await page.addInitScript(() => sessionStorage.setItem('vdi.accessToken', 'session-fixture'))
|
|
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[], total = items.length) => route.fulfill({ json: { items, total, page: 1, pageSize: 20, requestId: 'sessions-ui' } })
|
|
if (path === '/auth/me') { await route.fulfill({ json: { id: 'owner', username: 'root', displayName: '管理员', role: 'super_admin' } }); return }
|
|
if (path === '/settings') { await route.fulfill({ json: { mode: 'production', capabilities: ['administrator-sessions'], pveConfigured: true } }); return }
|
|
if (path === '/overview') { await route.fulfill({ json: { terminalCount: 0, onlineTerminals: 0, desktopCount: 0, runningDesktops: 0, activeSessions: 0, failedTasks: 0, mode: 'production' } }); return }
|
|
if (path === '/administrators') { await list([{ id: 'owner', username: 'root', displayName: '管理员', role: 'super_admin', enabled: true }, { id: 'other', username: 'maintainer', displayName: '运维', role: 'operator', enabled: true }]); return }
|
|
const match = path.match(/^\/administrators\/(owner|other)\/sessions(?:\/(.+))?$/)
|
|
if (match) {
|
|
const id = match[1]!, key = match[2]
|
|
if (request.method() === 'DELETE') {
|
|
revoked.push(`${id}/${key}`)
|
|
if (options.deny) { await route.fulfill({ status: 403, json: { code: 'forbidden', message: '当前账号没有该操作权限' } }); return }
|
|
await route.fulfill({ status: 204 }); return
|
|
}
|
|
queries.push(id)
|
|
if (options.fail && id === 'other') { await route.fulfill({ status: 500, json: { code: 'database_error', message: '登录列表暂不可用' } }); return }
|
|
if (options.slow && id === 'owner') await new Promise(resolve => setTimeout(resolve, 700))
|
|
const p = Number(url.searchParams.get('page') || 1), size = 20, total = 21 - revoked.filter(r => r.startsWith(`${id}/`)).length
|
|
await list(Array.from({ length: Math.max(0, Math.min(size, total - (p - 1) * size)) }, (_, i) => ({ key: `${id}-${(p - 1) * size + i}`, createdAt: i === 1 ? null : now, expiresAt: expires, clientIp: i === 1 ? '' : `192.0.2.${(p - 1) * size + i + 1}`, userAgent: 'Example browser <script>alert(1)</script>', current: id === 'owner' && p === 1 && i === 0 })), total); return
|
|
}
|
|
if (['/resources', '/desktops', '/users', '/terminals', '/tasks'].includes(path)) { await list([]); return }
|
|
await route.fulfill({ status: 404, json: { message: '测试接口未定义' } })
|
|
})
|
|
await page.goto('/settings'); await page.getByRole('button', { name: '管理员账号', exact: true }).click()
|
|
await expect(page.getByRole('heading', { name: '管理员账号', exact: true })).toBeVisible()
|
|
return { revoked, queries }
|
|
}
|
|
async function open(page: Page, name: string) {
|
|
await page.getByRole('row').filter({ hasText: name }).getByRole('button', { name: '在线登录', exact: true }).click()
|
|
return page.getByRole('dialog', { name: `在线登录 · ${name}`, exact: true })
|
|
}
|
|
|
|
test('登录来源、旧登录未知信息、分页、当前登录保护及深浅窄屏', async ({ page }, info) => {
|
|
const calls = await fixture(page), dialog = await open(page, 'root')
|
|
await expect(dialog.getByText('当前登录', { exact: true })).toBeVisible()
|
|
await expect(dialog.getByRole('row').filter({ hasText: '当前登录' }).getByRole('button', { name: '撤销登录' })).toBeDisabled()
|
|
await expect(dialog.getByText('升级前登录,时间未知', { exact: true })).toBeVisible()
|
|
await expect(dialog.getByText('升级前登录,来源未知', { exact: true })).toBeVisible()
|
|
await page.screenshot({ path: info.outputPath('admin-sessions-dark.png'), fullPage: true, animations: 'disabled' })
|
|
await dialog.locator('.el-pagination .btn-next').click(); await expect(dialog.getByText('192.0.2.21', { exact: true })).toBeVisible()
|
|
await dialog.getByRole('button', { name: '关闭', exact: true }).click()
|
|
await page.getByRole('button', { name: '切换浅色主题' }).click(); await page.setViewportSize({ width: 390, height: 844 })
|
|
await open(page, 'root'); await expect(dialog.getByText('当前登录', { exact: true })).toBeVisible()
|
|
expect(await page.evaluate(() => document.documentElement.scrollWidth)).toBeLessThanOrEqual(391)
|
|
await page.screenshot({ path: info.outputPath('admin-sessions-mobile.png'), fullPage: true, animations: 'disabled' })
|
|
expect(calls.revoked).toEqual([])
|
|
})
|
|
|
|
test('撤销指定登录需确认,取消不提交,成功刷新', async ({ page }) => {
|
|
const calls = await fixture(page), dialog = await open(page, 'maintainer')
|
|
await expect(dialog.getByText('共 21 次有效登录', { exact: true })).toBeVisible()
|
|
await dialog.getByRole('button', { name: '撤销登录', exact: true }).first().click()
|
|
await page.locator('.el-message-box').getByRole('button', { name: '取消', exact: true }).click()
|
|
expect(calls.revoked).toEqual([])
|
|
await dialog.getByRole('button', { name: '撤销登录', exact: true }).first().click()
|
|
await page.locator('.el-message-box').getByRole('button', { name: '确认提交', exact: true }).click()
|
|
await expect(dialog.getByText('共 20 次有效登录', { exact: true })).toBeVisible()
|
|
expect(calls.revoked).toEqual(['other/other-0'])
|
|
})
|
|
|
|
test('查询和撤销失败清除旧结果并显示错误,不误报成功', async ({ page }) => {
|
|
const calls = await fixture(page, { deny: true }), dialog = await open(page, 'maintainer')
|
|
await dialog.getByRole('button', { name: '撤销登录', exact: true }).first().click()
|
|
await page.locator('.el-message-box').getByRole('button', { name: '确认提交', exact: true }).click()
|
|
await expect(dialog.getByText('当前账号没有该操作权限', { exact: true })).toBeVisible()
|
|
await expect(dialog.getByRole('button', { name: '撤销登录', exact: true })).toHaveCount(0)
|
|
expect(calls.revoked).toHaveLength(1)
|
|
await page.unrouteAll(); await fixture(page, { fail: true }); await open(page, 'maintainer')
|
|
await expect(page.getByRole('dialog').getByText('登录列表暂不可用', { exact: true })).toBeVisible()
|
|
await expect(page.getByRole('dialog').getByRole('button', { name: '撤销登录', exact: true })).toHaveCount(0)
|
|
})
|
|
|
|
test('切换账号后旧请求不能覆盖新的登录列表', async ({ page }) => {
|
|
const calls = await fixture(page, { slow: true }), old = await open(page, 'root')
|
|
await expect.poll(() => calls.queries.includes('owner')).toBe(true)
|
|
await old.getByRole('button', { name: '关闭', exact: true }).click()
|
|
const current = await open(page, 'maintainer')
|
|
await expect(current.getByText('共 21 次有效登录', { exact: true })).toBeVisible()
|
|
await page.waitForTimeout(850)
|
|
await expect(current.getByText('当前登录', { exact: true })).toHaveCount(0)
|
|
expect(calls.revoked).toEqual([])
|
|
})
|