const { chromium, request } = require('/home/wren-dev/Porject/leaudit-platform/legal-platform-frontend/node_modules/playwright'); const FRONTEND = process.env.LEAUDIT_FRONTEND_URL || 'http://172.16.0.59:5173'; const BACKEND = process.env.LEAUDIT_BACKEND_URL || 'http://127.0.0.1:8096'; const runId = `pwr${Date.now()}`; const results = []; function record(name, ok, details = {}) { results.push({ name, ok, details }); console.log(`[${ok ? 'PASS' : 'FAIL'}] ${name}: ${JSON.stringify(details)}`); if (!ok) throw new Error(`${name}: ${JSON.stringify(details)}`); } async function asJson(response) { try { return await response.json(); } catch { return { text: await response.text() }; } } function unwrap(payload) { if (payload && typeof payload === 'object' && Object.prototype.hasOwnProperty.call(payload, 'data')) { return payload.data; } return payload; } async function apiFetch(api, method, path, token, options = {}) { const headers = { ...(options.headers || {}) }; if (token) headers.Authorization = `Bearer ${token}`; const response = await api.fetch(`${BACKEND}${path}`, { method, ...options, headers }); return { response, body: await asJson(response) }; } async function loginFrontendContext(browser) { const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } }); const loginResponse = await context.request.post(`${FRONTEND}/api/auth/password-login`, { data: { username: '000', password: 'admin06111' }, }); const loginBody = await asJson(loginResponse); if (!loginResponse.ok() || !loginBody?.access_token) { throw new Error(`前端登录失败 ${loginResponse.status()} ${JSON.stringify(loginBody)}`); } return { context, token: loginBody.access_token }; } async function createEntryModule(api, token) { const payload = { name: `PW验收政务入口-${runId}`, description: 'playwright remaining acceptance', path: '/govdoc/audits', route_path: '/govdoc/audits', menu_profile: 'govdoc', features: ['home', 'govdoc_audits', 'govdoc_upload', 'rule_groups'], tenants: [{ tenant_code: 'PUBLIC', tenant_name: '公共资源域', enabled: true, sort_order: 1 }], }; const { response, body } = await apiFetch(api, 'POST', '/api/v3/entry-modules', token, { data: payload }); record('创建名字不含公文的 govdoc 入口成功', response.status() === 200, { status: response.status(), body }); const module = unwrap(body); record('govdoc 入口保存 menu_profile/features', module.menu_profile === 'govdoc' && module.features?.includes('govdoc_audits'), module); return module; } async function createDocType(api, token, moduleId) { const payload = { code: `pw.remaining.${runId}`, name: `PW验收文档类型-${runId}`, description: 'playwright remaining acceptance', entryModuleId: Number(moduleId), isEnabled: true, sortOrder: 1, ruleSetIds: [], }; const { response, body } = await apiFetch(api, 'POST', '/api/document-types', token, { data: payload }); record('为验收入口创建文档类型成功', response.status() === 200, { status: response.status(), body }); return unwrap(body); } async function sidebarTexts(page) { return await page.locator('.sidebar a.sidebar-menu-item').evaluateAll((els) => ( els.map((el) => (el.textContent || '').replace(/\s+/g, ' ').trim()).filter(Boolean) )); } async function createCommonUserToken(api, adminToken) { const sub = `pw-no-image-${runId}`; const login = await apiFetch(api, 'POST', '/api/auth/login', null, { data: { userInfo: { sub, username: sub, nickname: `PW无图标权限-${runId}`, email: `${sub}@playwright.local`, phone_number: '13800000000', ou_id: 'pw-no-image', ou_name: 'Playwright验收组织', is_leader: false, }, area: '公共资源域', expiresIn: 3600, }, }); record('创建无图标权限测试用户登录成功', login.response.status() === 200, { status: login.response.status(), body: login.body }); const loginData = unwrap(login.body); const userId = Number(loginData.user_info?.user_id); const roles = await apiFetch(api, 'GET', '/api/v3/rbac/roles?page=1&page_size=200', adminToken); const roleItems = unwrap(roles.body)?.items || []; const commonRole = roleItems.find((item) => item.role_key === 'common'); record('找到 common 角色用于无权限验收', Boolean(commonRole?.id), { commonRole }); const assign = await apiFetch(api, 'POST', `/api/v3/rbac/users/${userId}/roles`, adminToken, { data: { role_ids: [Number(commonRole.id)] }, }); record('无图标权限测试用户分配 common 角色成功', assign.response.status() === 200, { status: assign.response.status(), body: assign.body }); return loginData.access_token; } async function main() { const browser = await chromium.launch({ headless: true }); const { context, token } = await loginFrontendContext(browser); const api = await request.newContext(); const page = await context.newPage(); const module = await createEntryModule(api, token); await createDocType(api, token, module.id); await page.goto(`${FRONTEND}/`, { waitUntil: 'domcontentloaded', timeout: 30000 }); await page.waitForLoadState('networkidle', { timeout: 20000 }).catch(() => {}); await page.getByRole('button', { name: module.name, exact: true }).click({ timeout: 15000 }); await page.waitForURL((url) => url.pathname === '/govdoc/audits' && url.searchParams.get('entryModuleId') === String(module.id), { timeout: 15000 }); await page.waitForLoadState('networkidle', { timeout: 20000 }).catch(() => {}); await page.waitForTimeout(1200); const side = await sidebarTexts(page); const headingText = await page.locator('.govdoc-audit-scope h2').first().textContent({ timeout: 10000 }); const storage = await page.evaluate(() => JSON.parse(sessionStorage.getItem('selectedEntryModuleContext') || 'null')); record('公文入口标题使用当前入口模块名称', headingText === `${module.name}文档列表`, { headingText, expected: `${module.name}文档列表` }); record('公文入口上下文保存为 govdoc', storage?.menuProfile === 'govdoc' && Number(storage?.id) === Number(module.id), storage); record('govdoc 侧栏显示公文列表/公文上传/规则分组', ['公文列表', '公文上传', '规则分组'].every((text) => side.includes(text)), { side }); record('govdoc 侧栏不显示普通文件上传/文档列表', !side.includes('文件上传') && !side.includes('文档列表'), { side }); const commonToken = await createCommonUserToken(api, token); const image403 = await apiFetch(api, 'POST', `/api/v3/entry-modules/${module.id}/image`, commonToken, { multipart: { file: { name: 'entry-module.png', mimeType: 'image/png', buffer: Buffer.from('89504e470d0a1a0a', 'hex'), }, }, }); record('无 entry_module:image:write 用户上传入口图标返回 403', image403.response.status() === 403, { status: image403.response.status(), body: image403.body }); await api.dispose(); await context.close(); await browser.close(); console.log('\nSUMMARY ' + JSON.stringify({ runId, pass: results.filter((r) => r.ok).length, fail: results.filter((r) => !r.ok).length, results }, null, 2)); } main().catch((err) => { console.error('\nERROR', err); console.log('\nSUMMARY ' + JSON.stringify({ runId, pass: results.filter((r) => r.ok).length, fail: results.filter((r) => !r.ok).length, results }, null, 2)); process.exit(1); });