import type { Page, Route } from '@playwright/test'; import { expect, test } from '@playwright/test'; const permissions = ['admin:user:view', 'admin:user:create', 'admin:update:view', 'admin:update:execute']; function envelope(data: unknown) { return { data, requestId: '01M00000000000000000000090' }; } function session(canExecute = true) { return { authenticated: true, user: { publicId: '01M00000000000000000000001', username: 'admin', displayName: '系统管理员', departmentName: '信息中心', mustChangePassword: false, }, roles: [{ code: 'SYSTEM_ADMIN', name: '系统管理员', workbenchRoute: '/governance/settings' }], activeRole: 'SYSTEM_ADMIN', permissions: canExecute ? permissions : permissions.filter((permission) => permission !== 'admin:update:execute'), }; } function updateView(state: string, checked: boolean, enabled = true, recoveryPending = false) { const busy = [ 'QUEUED', 'DOWNLOAD_QUEUED', 'INSTALL_QUEUED', 'VERIFYING', 'DOWNLOADING', 'BACKING_UP', 'INSTALLING', 'RUNNING', ].includes(state); return { enabled, currentVersion: '1.0.0-preview.1', latestVersion: checked ? '1.0.0-preview.2' : null, updateAvailable: checked && !['CURRENT', 'SUCCEEDED'].includes(state), state, message: !enabled ? '在线更新未配置' : recoveryPending ? '回滚未完成,更新服务将重试' : state === 'DOWNLOAD_QUEUED' ? '下载请求已排队' : state === 'INSTALL_QUEUED' ? '安装请求已排队' : state === 'READY' ? '更新包已下载并通过签名校验' : state === 'SUCCEEDED' ? '新版本已通过健康检查' : checked ? '发现新版本' : '尚未执行在线更新', releaseNotes: checked ? 'Preview update' : null, publishedAt: checked ? '2026-08-16T00:00:00Z' : null, checkedAt: checked ? '2026-08-16T00:01:00Z' : null, statusUpdatedAt: null, allowedActions: !enabled || recoveryPending || busy ? [] : state === 'READY' ? ['CHECK', 'INSTALL'] : checked ? ['CHECK', 'DOWNLOAD'] : ['CHECK'], }; } interface FixtureOptions { canExecute?: boolean; failDownloadResponse?: boolean; failInstallResponse?: boolean; } async function installFixture( page: Page, downloadBodies: unknown[], installBodies: unknown[], enabled = true, recoveryPending = false, completeAfterInstall = false, initialState?: string, completeBusyAfterFirstRead = false, options: FixtureOptions = {}, ) { const { canExecute = true, failDownloadResponse = false, failInstallResponse = false } = options; let checked = recoveryPending || Boolean(initialState); let state = recoveryPending ? 'FAILED' : initialState || 'IDLE'; const history: Array> = []; const recordHistory = (actionCode: string, targetVersion = '1.0.0-preview.2') => { history.unshift({ publicId: `01M00000000000000000000${String(history.length + 1).padStart(3, '0')}`, eventSequence: history.length + 1, requestId: '01M00000000000000000000092', username: 'admin', activeRole: 'SYSTEM_ADMIN', actionCode, objectType: 'SYSTEM_UPDATE', objectPublicId: 'SYSTEM_UPDATE', resultCode: 'SUCCESS', reason: actionCode === 'SYSTEM_UPDATE_REQUEST' ? '管理员确认立即更新并重启' : null, beforeJson: null, afterJson: JSON.stringify({ targetVersion }), occurredAt: '2026-08-16T00:02:00Z', allowedActions: [], }); }; await page.route('**/api/v1/**', async (route: Route) => { const request = route.request(); const pathname = new URL(request.url()).pathname; if (pathname === '/api/v1/auth/session') return route.fulfill({ json: envelope(session(canExecute)) }); if (pathname === '/api/v1/auth/profile') return route.fulfill({ json: envelope(session(canExecute).user) }); if (pathname === '/api/v1/auth/roles') return route.fulfill({ json: envelope(session(canExecute).roles) }); if (pathname === '/api/v1/auth/csrf') { return route.fulfill({ headers: { 'set-cookie': 'XSRF-TOKEN=update-csrf; Path=/; SameSite=Lax' }, json: envelope({ token: 'update-csrf' }), }); } if (pathname === '/api/v1/admin/users') { return route.fulfill({ json: { data: [], meta: { page: 1, size: 20, totalElements: 0, totalPages: 0 }, requestId: '01M00000000000000000000091', }, }); } if (pathname === '/api/v1/admin/system-update' && request.method() === 'GET') { const response = updateView(state, checked, enabled, recoveryPending); if (completeBusyAfterFirstRead && state === 'INSTALLING') state = 'SUCCEEDED'; return route.fulfill({ json: envelope(response) }); } if (pathname === '/api/v1/admin/system-update/check' && request.method() === 'POST') { checked = true; recordHistory('SYSTEM_UPDATE_CHECK'); const response = updateView(state, checked, enabled, recoveryPending); if (!canExecute) response.allowedActions = ['CHECK']; return route.fulfill({ json: envelope(response) }); } if (pathname === '/api/v1/admin/system-update/download' && request.method() === 'POST') { downloadBodies.push(request.postDataJSON()); recordHistory('SYSTEM_UPDATE_DOWNLOAD_REQUEST'); state = 'READY'; if (failDownloadResponse) return route.abort('connectionreset'); return route.fulfill({ json: envelope(updateView('DOWNLOAD_QUEUED', true, enabled)) }); } if (pathname === '/api/v1/admin/system-update/install' && request.method() === 'POST') { installBodies.push(request.postDataJSON()); recordHistory('SYSTEM_UPDATE_REQUEST'); const queued = updateView('INSTALL_QUEUED', true, enabled); state = failInstallResponse || completeAfterInstall ? 'SUCCEEDED' : 'INSTALL_QUEUED'; if (failInstallResponse) return route.abort('connectionreset'); return route.fulfill({ json: envelope(queued) }); } if (pathname === '/api/v1/audit/logs' && request.method() === 'GET') { return route.fulfill({ json: { data: history, meta: { page: 1, size: 20, totalElements: history.length, totalPages: history.length ? 1 : 0 }, requestId: '01M00000000000000000000093', }, }); } if (request.method() === 'GET') return route.fulfill({ json: envelope([]) }); return route.fulfill({ status: 204 }); }); } test('system administrator checks, downloads, and explicitly installs a signed online update', async ({ page }) => { const downloadBodies: unknown[] = []; const installBodies: unknown[] = []; await installFixture(page, downloadBodies, installBodies); await page.goto('/governance/update'); await expect(page.locator('h1.page-title-sr-only')).toHaveText('系统更新'); await expect(page.locator('.version-item').filter({ hasText: '当前版本' })).toContainText('1.0.0-preview.1'); await page.getByRole('button', { name: '获取版本', exact: true }).click(); await expect(page.locator('.version-item').filter({ hasText: '最新版本' })).toContainText('1.0.0-preview.2'); await expect(page.getByRole('button', { name: '立即更新', exact: true })).toBeVisible({ timeout: 5_000 }); await expect(page.getByRole('button', { name: '立即更新并重启', exact: true })).toHaveCount(0); expect(downloadBodies).toEqual([]); await page.getByRole('button', { name: '立即更新', exact: true }).click(); expect(downloadBodies).toEqual([{ version: '1.0.0-preview.2' }]); const updateState = page.locator('.version-item').filter({ hasText: '更新状态' }); await expect(updateState.getByText('下载已排队', { exact: true })).toBeVisible(); await expect(updateState.getByText('下载完成', { exact: true })).toBeVisible({ timeout: 8_000 }); await expect(page.getByRole('button', { name: '立即更新并重启', exact: true })).toBeVisible(); await page.getByRole('button', { name: '立即更新并重启', exact: true }).click(); await expect(page.getByText('安装请求已排队', { exact: true })).toBeVisible(); expect(installBodies).toEqual([{ version: '1.0.0-preview.2', reason: '管理员确认立即更新并重启' }]); }); test('rechecking a downloaded package prompts installation instead of downloading again', async ({ page }) => { await installFixture(page, [], [], true, false, false, 'READY'); await page.goto('/governance/update'); await page.getByRole('button', { name: '获取版本', exact: true }).click(); await expect(page.getByText('更新包已下载完成,请点击“立即更新并重启”安装新版本', { exact: true })).toBeVisible(); await expect(page.getByRole('button', { name: '立即更新', exact: true })).toHaveCount(0); await expect(page.getByRole('button', { name: '立即更新并重启', exact: true })).toBeVisible(); }); test('view-only administrators see a download permission message', async ({ page }) => { await installFixture(page, [], [], true, false, false, undefined, false, { canExecute: false }); await page.goto('/governance/update'); await page.getByRole('button', { name: '获取版本', exact: true }).click(); await expect(page.getByText('发现新版本,但当前账号没有下载权限', { exact: true })).toBeVisible(); await expect(page.getByRole('button', { name: '立即更新', exact: true })).toHaveCount(0); await expect(page.getByRole('button', { name: '立即更新并重启', exact: true })).toHaveCount(0); }); test('download and install continue polling after an accepted command loses its response', async ({ page }) => { await installFixture(page, [], [], true, false, false, undefined, false, { failDownloadResponse: true, }); await page.goto('/governance/update'); await page.getByRole('button', { name: '获取版本', exact: true }).click(); await page.getByRole('button', { name: '立即更新', exact: true }).click(); await expect( page.locator('.version-item').filter({ hasText: '更新状态' }).getByText('下载完成', { exact: true }), ).toBeVisible({ timeout: 8_000 }); await expect(page.getByRole('button', { name: '立即更新并重启', exact: true })).toBeVisible(); }); test('accepted install continues to the refresh countdown after its response is lost', async ({ page }) => { await installFixture(page, [], [], true, false, false, undefined, false, { failInstallResponse: true, }); await page.goto('/governance/update'); await page.getByRole('button', { name: '获取版本', exact: true }).click(); await page.getByRole('button', { name: '立即更新', exact: true }).click(); await expect( page.locator('.version-item').filter({ hasText: '更新状态' }).getByText('下载完成', { exact: true }), ).toBeVisible({ timeout: 8_000 }); await page.getByRole('button', { name: '立即更新并重启', exact: true }).click(); await expect(page.getByText('新版本已通过健康检查,页面将在 10 秒后自动刷新。', { exact: true })).toBeVisible({ timeout: 8_000, }); }); test('brand logo returns the active identity to its workbench', async ({ page }) => { await installFixture(page, [], []); await page.goto('/governance/update'); await page.locator('.brand-logo').click(); await expect(page).toHaveURL(/\/governance\/settings$/); await expect(page.getByText('选择工作身份', { exact: true })).toHaveCount(0); }); test('disabled online updates expose status without an executable check action', async ({ page }) => { await installFixture(page, [], [], false); await page.goto('/governance/update'); await expect(page.getByText('在线更新未配置', { exact: true })).toBeVisible(); await expect(page.getByRole('button', { name: '获取版本', exact: true })).toBeDisabled(); await expect(page.getByRole('button', { name: '立即更新', exact: true })).toHaveCount(0); await expect(page.getByRole('button', { name: '立即更新并重启', exact: true })).toHaveCount(0); }); test('rollback recovery keeps the failure visible without an install action', async ({ page }) => { await installFixture(page, [], [], true, true); await page.goto('/governance/update'); await expect(page.getByText('回滚未完成,更新服务将重试', { exact: true })).toBeVisible(); await expect(page.getByRole('button', { name: '立即更新并重启', exact: true })).toHaveCount(0); }); test('successful installation starts the ten-second automatic refresh countdown', async ({ page }) => { await installFixture(page, [], [], true, false, true); await page.goto('/governance/update'); await page.getByRole('button', { name: '获取版本', exact: true }).click(); await page.getByRole('button', { name: '立即更新', exact: true }).click({ timeout: 5_000 }); await expect( page.locator('.version-item').filter({ hasText: '更新状态' }).getByText('下载完成', { exact: true }), ).toBeVisible({ timeout: 8_000 }); await page.getByRole('button', { name: '立即更新并重启', exact: true }).click(); await expect(page.getByText('新版本已通过健康检查,页面将在 10 秒后自动刷新。', { exact: true })).toBeVisible({ timeout: 5_000, }); }); test('reloading during installation resumes polling and starts the refresh countdown', async ({ page }) => { await installFixture(page, [], [], true, false, false, 'INSTALLING', true); await page.goto('/governance/update'); await expect( page.locator('.version-item').filter({ hasText: '更新状态' }).getByText('安装中', { exact: true }), ).toBeVisible(); await expect(page.getByText('新版本已通过健康检查,页面将在 10 秒后自动刷新。', { exact: true })).toBeVisible({ timeout: 6_000, }); });