diff --git a/reports/digital-human-simple-auth-20260909/01-configuration.png b/reports/digital-human-simple-auth-20260909/01-configuration.png new file mode 100644 index 0000000..d37cc18 Binary files /dev/null and b/reports/digital-human-simple-auth-20260909/01-configuration.png differ diff --git a/reports/digital-human-simple-auth-20260909/02-connection.png b/reports/digital-human-simple-auth-20260909/02-connection.png new file mode 100644 index 0000000..6d50186 Binary files /dev/null and b/reports/digital-human-simple-auth-20260909/02-connection.png differ diff --git a/reports/digital-human-simple-auth-20260909/index.html b/reports/digital-human-simple-auth-20260909/index.html new file mode 100644 index 0000000..e924160 --- /dev/null +++ b/reports/digital-human-simple-auth-20260909/index.html @@ -0,0 +1 @@ +数字人 APPID + Secret 简化验证

数字人 APPID + Secret 简化验证

2026-09-09 · 开发环境真实联调 · 18 项通过

移除两个额外密钥文件及运行依赖;现有 Secret 迁移至接口配置表 app_secret。继续使用现有登录身份调用 Auth,数字人开放服务通过 APPID + Secret 换取短期令牌。

后端 mvn clean package 通过;新增认证、密钥保存和转发测试。039 迁移的密文保护、正常迁移、重复执行已验证。原有应用保持不变;本轮未重新创建视频生成任务。

配置弹窗

真实连接测试

\ No newline at end of file diff --git a/reports/digital-human-simple-auth-20260909/results.json b/reports/digital-human-simple-auth-20260909/results.json new file mode 100644 index 0000000..45b2f0f --- /dev/null +++ b/reports/digital-human-simple-auth-20260909/results.json @@ -0,0 +1,77 @@ +{ + "time": "2026-09-09T15:49:58.729Z", + "checks": [ + { + "name": "integration-secret.key 已移除", + "result": "PASS" + }, + { + "name": "integration-internal.key 已移除", + "result": "PASS" + }, + { + "name": "接口配置列表不返回 Secret 字段", + "result": "PASS" + }, + { + "name": "原有 APPID 和密钥配置保留", + "result": "PASS" + }, + { + "name": "留空 Secret 保存配置成功且不回显密钥", + "result": "PASS" + }, + { + "name": "移除密钥文件后 APPID + Secret 连接测试成功", + "result": "PASS" + }, + { + "name": "内部调用继续要求现有登录身份", + "result": "PASS" + }, + { + "name": "复用登录身份获取运行令牌,无额外服务密钥", + "result": "PASS" + }, + { + "name": "Tran → Auth → 数字人系统能力调用成功", + "result": "PASS" + }, + { + "name": "授权资源 videos 查询成功", + "result": "PASS" + }, + { + "name": "授权资源 video-folders 查询成功", + "result": "PASS" + }, + { + "name": "授权资源 avatars 查询成功", + "result": "PASS" + }, + { + "name": "授权资源 voices 查询成功", + "result": "PASS" + }, + { + "name": "授权资源 backgrounds 查询成功", + "result": "PASS" + }, + { + "name": "配置弹窗密钥留空,仅显示已配置状态", + "result": "PASS" + }, + { + "name": "页面留空密钥保存成功", + "result": "PASS" + }, + { + "name": "页面连接检查实际响应成功", + "result": "PASS" + }, + { + "name": "页面连接测试成功", + "result": "PASS" + } + ] +} \ No newline at end of file diff --git a/reports/guide-style-20260909/01-list.png b/reports/guide-style-20260909/01-list.png new file mode 100644 index 0000000..e89e548 Binary files /dev/null and b/reports/guide-style-20260909/01-list.png differ diff --git a/reports/guide-style-20260909/02-list-actions.png b/reports/guide-style-20260909/02-list-actions.png new file mode 100644 index 0000000..811a099 Binary files /dev/null and b/reports/guide-style-20260909/02-list-actions.png differ diff --git a/reports/guide-style-20260909/03-editor-dark.png b/reports/guide-style-20260909/03-editor-dark.png new file mode 100644 index 0000000..e1cf9b2 Binary files /dev/null and b/reports/guide-style-20260909/03-editor-dark.png differ diff --git a/reports/guide-style-20260909/03-editor.png b/reports/guide-style-20260909/03-editor.png new file mode 100644 index 0000000..933cfcf Binary files /dev/null and b/reports/guide-style-20260909/03-editor.png differ diff --git a/reports/guide-style-20260909/03-guide-preview.png b/reports/guide-style-20260909/03-guide-preview.png new file mode 100644 index 0000000..3ed333e Binary files /dev/null and b/reports/guide-style-20260909/03-guide-preview.png differ diff --git a/reports/guide-style-20260909/04-templates.png b/reports/guide-style-20260909/04-templates.png new file mode 100644 index 0000000..c9a8bda Binary files /dev/null and b/reports/guide-style-20260909/04-templates.png differ diff --git a/reports/guide-style-20260909/05-template-preview.png b/reports/guide-style-20260909/05-template-preview.png new file mode 100644 index 0000000..2054b94 Binary files /dev/null and b/reports/guide-style-20260909/05-template-preview.png differ diff --git a/reports/guide-style-20260909/06-api.png b/reports/guide-style-20260909/06-api.png new file mode 100644 index 0000000..8ee4fc5 Binary files /dev/null and b/reports/guide-style-20260909/06-api.png differ diff --git a/reports/guide-style-20260909/07-api-result.png b/reports/guide-style-20260909/07-api-result.png new file mode 100644 index 0000000..8ab66b4 Binary files /dev/null and b/reports/guide-style-20260909/07-api-result.png differ diff --git a/reports/guide-style-20260909/08-api-dark.png b/reports/guide-style-20260909/08-api-dark.png new file mode 100644 index 0000000..eb57b82 Binary files /dev/null and b/reports/guide-style-20260909/08-api-dark.png differ diff --git a/reports/guide-style-20260909/08-api-graphite.png b/reports/guide-style-20260909/08-api-graphite.png new file mode 100644 index 0000000..0f7d814 Binary files /dev/null and b/reports/guide-style-20260909/08-api-graphite.png differ diff --git a/reports/guide-style-20260909/08-api-technology.png b/reports/guide-style-20260909/08-api-technology.png new file mode 100644 index 0000000..a0a9daf Binary files /dev/null and b/reports/guide-style-20260909/08-api-technology.png differ diff --git a/reports/guide-style-20260909/09-api-laptop.png b/reports/guide-style-20260909/09-api-laptop.png new file mode 100644 index 0000000..242a23c Binary files /dev/null and b/reports/guide-style-20260909/09-api-laptop.png differ diff --git a/reports/guide-style-20260909/10-templates-laptop.png b/reports/guide-style-20260909/10-templates-laptop.png new file mode 100644 index 0000000..c65722f Binary files /dev/null and b/reports/guide-style-20260909/10-templates-laptop.png differ diff --git a/reports/guide-style-20260909/11-templates-dark.png b/reports/guide-style-20260909/11-templates-dark.png new file mode 100644 index 0000000..bdf9205 Binary files /dev/null and b/reports/guide-style-20260909/11-templates-dark.png differ diff --git a/reports/guide-style-20260909/12-list-dark.png b/reports/guide-style-20260909/12-list-dark.png new file mode 100644 index 0000000..98fb7af Binary files /dev/null and b/reports/guide-style-20260909/12-list-dark.png differ diff --git a/reports/guide-style-20260909/13-create-wizard.png b/reports/guide-style-20260909/13-create-wizard.png new file mode 100644 index 0000000..ed5d1d5 Binary files /dev/null and b/reports/guide-style-20260909/13-create-wizard.png differ diff --git a/reports/guide-style-20260909/index.html b/reports/guide-style-20260909/index.html new file mode 100644 index 0000000..648b7fe --- /dev/null +++ b/reports/guide-style-20260909/index.html @@ -0,0 +1,36 @@ + + +作业指导页面风格统一 · 验证报告 + +
构建与浏览器检查通过

作业指导页面风格统一

2026-09-09 · 数字车间 / 内容制作

参照模型制作、场景制作的系统主题,统一指导书列表、模板库、编制、新建、预览和对外接口页面。

+

本次调整

补充调整:模板库以完整白色内容卡片承载模板、分页与结构预览;对外接口的主内容、接口说明及契约区域统一使用系统白色背景。截图已更新,并重新通过构建与浏览器回归。

+ + + +
页面调整内容
指导书列表使用系统搜索框、状态/排序选择器、带边框表格和统一分页;搜索需明确提交,支持重置。进入编制为主要操作,详情、预览、版本、下载和删除收进“更多”,权限及禁用条件保留。
模板库移除重复大标题,统一页面边距、模板卡片、选中态和分页;较窄窗口改为两列,结构预览保留。
编制、新建、预览统一工具栏、按钮、表单、侧栏和选中态颜色。编制器按视口分配高度,内部滚动。文档纸张及插图保留原有颜色。
对外接口统一三栏面板、留白、输入框及按钮;长路径换行,较窄窗口将契约说明放到下方。暗色主题补齐背景与文字对比。
+

验证范围及结果

+ + + + + + +
检查结果
pnpm run build:类型检查、Vite 构建、模型编辑器产物检查通过
12 条临时指导书:明确搜索、无结果、重置、下一页/上一页通过
更多菜单、项目详情打开/关闭、进入编制、指导书预览、新建入口通过
4 个模板:选择切换、结构预览、模板预览弹窗通过
接口查询:执行 GET 项目列表,自检返回 200通过
1920×911、1366×768;军绿、科技、石墨、暗色主题截图检查通过
所有截取页面无整体横向溢出;无浏览器未捕获异常;局域网入口返回 200通过
+

使用独立、用后关闭的浏览器上下文,在其本地指导书仓库准备 STYLE-1~STYLE-12 测试数据,没有修改用户浏览器中的现有指导书。未执行发布、删除、外部写入或下载生成的全面业务回归。本轮为样式调整与主要交互回归;接口页现有查询仍是本地工程自检,不代表新增了 HTTP 服务。

+

编制器和模板预览保留现有自定义渲染及事件处理,宿主页负责系统控件和主题适配,避免样式调整影响文档编制与导出逻辑。构建保留现有大分包、Three.js URL 提示,无新增构建失败。

+
指导书列表:统一筛选、表格和主要操作
指导书列表
+
更多菜单:保留全部次要操作及禁用状态
列表更多菜单
+
模板库:统一外边距、卡片、分页和结构预览
模板库
+
模板预览
模板预览
+
指导书编制
指导书编制
+
指导书编制 · 暗色主题
暗色编制器
+
指导书动态预览:纸张、插图保持原色
指导书预览
+
对外接口:导航、参数及契约说明
对外接口
+
接口查询结果
接口查询结果
+
暗色主题下的接口页面
暗色接口
+
较窄窗口:模板库自动调整列数
较窄模板库
+
新建指导书入口
新建指导书
+

更多主题截图

+
diff --git a/reports/guide-style-20260909/results.json b/reports/guide-style-20260909/results.json new file mode 100644 index 0000000..86751d8 --- /dev/null +++ b/reports/guide-style-20260909/results.json @@ -0,0 +1,94 @@ +{ + "checks": [ + { + "name": "01-list", + "result": "PASS", + "screenshot": "01-list.png" + }, + { + "name": "02-list-actions", + "result": "PASS", + "screenshot": "02-list-actions.png" + }, + { + "name": "03-editor", + "result": "PASS", + "screenshot": "03-editor.png" + }, + { + "name": "03-editor-dark", + "result": "PASS", + "screenshot": "03-editor-dark.png" + }, + { + "name": "03-guide-preview", + "result": "PASS", + "screenshot": "03-guide-preview.png" + }, + { + "name": "04-templates", + "result": "PASS", + "screenshot": "04-templates.png" + }, + { + "name": "05-template-preview", + "result": "PASS", + "screenshot": "05-template-preview.png" + }, + { + "name": "06-api", + "result": "PASS", + "screenshot": "06-api.png" + }, + { + "name": "07-api-result", + "result": "PASS", + "screenshot": "07-api-result.png" + }, + { + "name": "08-api-technology", + "result": "PASS", + "screenshot": "08-api-technology.png" + }, + { + "name": "08-api-graphite", + "result": "PASS", + "screenshot": "08-api-graphite.png" + }, + { + "name": "08-api-dark", + "result": "PASS", + "screenshot": "08-api-dark.png" + }, + { + "name": "09-api-laptop", + "result": "PASS", + "screenshot": "09-api-laptop.png" + }, + { + "name": "10-templates-laptop", + "result": "PASS", + "screenshot": "10-templates-laptop.png" + }, + { + "name": "11-templates-dark", + "result": "PASS", + "screenshot": "11-templates-dark.png" + }, + { + "name": "12-list-dark", + "result": "PASS", + "screenshot": "12-list-dark.png" + }, + { + "name": "13-create-wizard", + "result": "PASS", + "screenshot": "13-create-wizard.png" + }, + { + "name": "搜索/重置、详情、编制入口、模板选择/预览、GET 接口自检、主题切换、局域网入口", + "result": "PASS" + } + ], + "errors": [] +} \ No newline at end of file diff --git a/reports/interface-dialog-height-20260909/1280x600-dark.png b/reports/interface-dialog-height-20260909/1280x600-dark.png new file mode 100644 index 0000000..33c19f8 Binary files /dev/null and b/reports/interface-dialog-height-20260909/1280x600-dark.png differ diff --git a/reports/interface-dialog-height-20260909/1366x768-military.png b/reports/interface-dialog-height-20260909/1366x768-military.png new file mode 100644 index 0000000..2073bb4 Binary files /dev/null and b/reports/interface-dialog-height-20260909/1366x768-military.png differ diff --git a/reports/interface-dialog-height-20260909/390x844-military.png b/reports/interface-dialog-height-20260909/390x844-military.png new file mode 100644 index 0000000..d781f83 Binary files /dev/null and b/reports/interface-dialog-height-20260909/390x844-military.png differ diff --git a/reports/interface-dialog-height-20260909/844x390-military.png b/reports/interface-dialog-height-20260909/844x390-military.png new file mode 100644 index 0000000..3cb3962 Binary files /dev/null and b/reports/interface-dialog-height-20260909/844x390-military.png differ diff --git a/reports/interface-dialog-height-20260909/index.html b/reports/interface-dialog-height-20260909/index.html new file mode 100644 index 0000000..70979c0 --- /dev/null +++ b/reports/interface-dialog-height-20260909/index.html @@ -0,0 +1 @@ +Service configuration dialog

Service configuration dialog

2026-09-09. Local and LAN development environment. Account: root. 4 viewport checks passed: dialog stays inside viewport, form scrolls to final field, footer remains fixed and visible, cancel works. No business data modified.

1366x768 / military / PASS

1280x600 / dark / PASS

390x844 / military / PASS

844x390 / military / PASS

\ No newline at end of file diff --git a/reports/interface-dialog-height-20260909/results.json b/reports/interface-dialog-height-20260909/results.json new file mode 100644 index 0000000..e978d9d --- /dev/null +++ b/reports/interface-dialog-height-20260909/results.json @@ -0,0 +1,31 @@ +{ + "checks": [ + { + "viewport": "1366x768", + "theme": "military", + "result": "PASS", + "screenshot": "1366x768-military.png" + }, + { + "viewport": "1280x600", + "theme": "dark", + "result": "PASS", + "screenshot": "1280x600-dark.png" + }, + { + "viewport": "390x844", + "theme": "military", + "result": "PASS", + "screenshot": "390x844-military.png" + }, + { + "viewport": "844x390", + "theme": "military", + "result": "PASS", + "screenshot": "844x390-military.png" + } + ], + "lan": "PASS", + "account": "root", + "dataChanges": "none" +} \ No newline at end of file diff --git a/reports/interface-dialog-height-build.log b/reports/interface-dialog-height-build.log new file mode 100644 index 0000000..93a7009 Binary files /dev/null and b/reports/interface-dialog-height-build.log differ diff --git a/tools/digital-human-simple-auth.mjs b/tools/digital-human-simple-auth.mjs new file mode 100644 index 0000000..2eccc8d --- /dev/null +++ b/tools/digital-human-simple-auth.mjs @@ -0,0 +1,77 @@ +import fs from 'node:fs' +import path from 'node:path' +import assert from 'node:assert/strict' +import { fileURLToPath } from 'node:url' +import { createRequire } from 'node:module' +import { login, call } from './role-permissions-live.mjs' + +const report = fileURLToPath(new URL('../reports/digital-human-simple-auth-20260909/', import.meta.url)) +fs.mkdirSync(report, { recursive:true }) +const checks = [] +function check(name, condition) { assert.ok(condition, name); checks.push({ name, result:'PASS' }); console.log(`PASS: ${name}`) } +const clean = value => !/"(?:appSecret|secretCipher|app_secret|secret_cipher)"\s*:/.test(JSON.stringify(value)) +let browser +try { + for (const name of ['integration-secret.key', 'integration-internal.key']) { + check(`${name} 已移除`, !fs.existsSync(fileURLToPath(new URL(`../../unreal_tran_api/config/${name}`, import.meta.url)))) + } + const session = await login('root') + const token = session.accessToken + const configs = await call('auth/v1/interfaces?size=100', token) + check('接口配置列表不返回 Secret 字段', configs.status === 200 && clean(configs.data)) + const row = configs.data.records.find(item => item.code === 'digital-human' && item.environmentCode === 'default') + assert.ok(row, 'Digital-human configuration exists') + const settings = JSON.parse(row.extraConfig) + check('原有 APPID 和密钥配置保留', settings.appId === 'AP_238179E0A10F97927EA49FA3' && settings.secretConfigured) + const configuration = { ...row, environment:row.environmentCode, version:row.dataVersion } + const saved = await call(`auth/v1/interfaces/${row.id}/digital-human`, token, { configuration, appId:settings.appId, appSecret:'' }, 'PUT') + check('留空 Secret 保存配置成功且不回显密钥', saved.status === 200 && saved.code === 0 && clean(saved.data)) + const tested = await call(`auth/v1/interfaces/${row.id}/test`, token, {}) + check('移除密钥文件后 APPID + Secret 连接测试成功', tested.status === 200 && tested.data?.success) + const runtimeUrl = 'http://127.0.0.1:6101/internal/v1/interfaces/digital-human/runtime' + const anonymous = await fetch(runtimeUrl, { method:'POST' }) + check('内部调用继续要求现有登录身份', anonymous.status === 401) + const runtime = await fetch(runtimeUrl, { method:'POST', headers:{ Authorization:`Bearer ${token}` } }) + const runtimeData = await runtime.json() + check('复用登录身份获取运行令牌,无额外服务密钥', runtime.status === 200 && runtimeData.data?.accessToken && clean(runtimeData) && runtime.headers.get('cache-control') === 'no-store') + const capabilities = await call('tran/v1/content/digital-human/capabilities', token) + check('Tran → Auth → 数字人系统能力调用成功', capabilities.status === 200 && capabilities.data?.appId === settings.appId) + for (const kind of ['videos', 'video-folders', 'avatars', 'voices', 'backgrounds']) { + const response = await call(`tran/v1/content/digital-human/resources/${kind}`, token) + check(`授权资源 ${kind} 查询成功`, response.status === 200 && response.code === 0) + } + const { chromium, expect } = createRequire(new URL('../package.json', import.meta.url))('@playwright/test') + browser = await chromium.launch({ channel:'msedge', headless:true }) + const context = await browser.newContext({ viewport:{ width:1600, height:1000 } }) + await context.addInitScript(({ accessToken, refreshToken }) => { + sessionStorage.setItem('unreal-tran:web:access-token:v1', accessToken) + sessionStorage.setItem('unreal-tran:web:refresh-token:v1', refreshToken) + }, session) + const page = await context.newPage() + await page.goto('http://127.0.0.1:6180/system/interfaces') + const configRow = page.locator('.el-table__body tr').filter({ hasText:'digital-human' }).first() + await expect(configRow).toBeVisible({ timeout:60000 }) + await configRow.getByRole('button', { name:'编辑配置' }).click() + const dialog = page.getByRole('dialog', { name:'编辑服务配置' }) + const secret = dialog.locator('input[type="password"]') + await expect(secret).toHaveValue('') + await expect(secret).toHaveAttribute('placeholder', '已配置,留空保留现有密钥') + check('配置弹窗密钥留空,仅显示已配置状态', true) + await page.screenshot({ path:path.join(report, '01-configuration.png'), animations:'disabled' }) + const saveResponse = page.waitForResponse(response => response.request().method() === 'PUT' && response.url().endsWith(`/interfaces/${row.id}/digital-human`)) + await dialog.getByRole('button', { name:'保存配置' }).click() + check('页面留空密钥保存成功', (await saveResponse).status() === 200) + await expect(dialog).toBeHidden() + const testResponse = page.waitForResponse(response => response.request().method() === 'POST' && response.url().endsWith(`/interfaces/${row.id}/test`)) + await configRow.getByRole('button', { name:'连接检查' }).click() + const testedFromPage = await (await testResponse).json() + check('页面连接检查实际响应成功', testedFromPage.data?.success) + await expect(configRow.getByText('连接正常', { exact:true })).toBeVisible({ timeout:30000 }) + await expect(page.locator('.el-loading-mask:visible')).toHaveCount(0) + await page.screenshot({ path:path.join(report, '02-connection.png'), animations:'disabled' }) + check('页面连接测试成功', true) +} finally { + await browser?.close() + fs.writeFileSync(path.join(report, 'results.json'), JSON.stringify({ time:new Date().toISOString(), checks }, null, 2)) +} +fs.writeFileSync(path.join(report, 'index.html'), `数字人 APPID + Secret 简化验证

数字人 APPID + Secret 简化验证

2026-09-09 · 开发环境真实联调 · ${checks.length} 项通过

移除两个额外密钥文件及运行依赖;现有 Secret 迁移至接口配置表 app_secret。继续使用现有登录身份调用 Auth,数字人开放服务通过 APPID + Secret 换取短期令牌。

后端 mvn clean package 通过;新增认证、密钥保存和转发测试。039 迁移的密文保护、正常迁移、重复执行已验证。原有应用保持不变;本轮未重新创建视频生成任务。

配置弹窗

真实连接测试

`) diff --git a/tools/guide-style-review.mjs b/tools/guide-style-review.mjs new file mode 100644 index 0000000..a59f9b3 --- /dev/null +++ b/tools/guide-style-review.mjs @@ -0,0 +1,114 @@ +import fs from 'node:fs' +import path from 'node:path' +import { createRequire } from 'node:module' +import { fileURLToPath } from 'node:url' +import assert from 'node:assert/strict' +import { login } from './role-permissions-live.mjs' +const { chromium, expect } = createRequire(new URL('../package.json', import.meta.url))('@playwright/test') +const report = fileURLToPath(new URL('../reports/guide-style-20260909/', import.meta.url)) +fs.mkdirSync(report, { recursive:true }) +const browser = await chromium.launch({ channel:'msedge', headless:true, args:['--enable-unsafe-swiftshader'] }) +const checks = [], errors = [] +const session = await login('root') +const context = await browser.newContext({ viewport:{ width:1920, height:911 } }) +await context.addInitScript(({ accessToken, refreshToken }) => { + sessionStorage.setItem('unreal-tran:web:access-token:v1', accessToken) + sessionStorage.setItem('unreal-tran:web:refresh-token:v1', refreshToken) +}, session) +const page = await context.newPage() +page.on('pageerror', error => errors.push(error.message)) +async function shot(name) { + const overflow = await page.locator('.legacy-guide-page').evaluate(el => el.scrollWidth > el.clientWidth + 2) + assert.equal(overflow, false, `${name}: page has no horizontal overflow`) + await page.screenshot({ path:path.join(report, `${name}.png`), animations:'disabled' }) + checks.push({ name, result:'PASS', screenshot:`${name}.png` }) +} +async function theme(mode) { + await page.evaluate(async mode => (await import('/src/stores/preferences.ts')).usePreferencesStore().setThemeMode(mode), mode) +} +try { + await page.goto('http://127.0.0.1:6180/content/ofd') + await expect(page.locator('.guide-project-list')).toBeVisible({ timeout:60000 }) + await theme('military') + // Fixtures exist only in this disposable browser's local guide repository. + await page.evaluate(async () => { + const { legacyGuideContentStore } = await import('/src/features/guide-legacy/runtime.ts') + for (let i=1;i<=12;i++) legacyGuideContentStore.createOFDProject({ id:`STYLE-${i}`, code:`STYLE-${String(i).padStart(3,'0')}`, title:`液压系统检查作业指导书 · 样式验证 ${i}`, author:'样式验证' }) + }) + await expect(page.locator('.guide-project-title')).toHaveCount(10) + console.log('List loaded', await page.locator('.guide-project-title').count()) + await shot('01-list') + const count = await page.locator('.guide-project-title').count() + await page.getByRole('textbox', { name:'搜索指导书', exact:true }).fill('NO-MATCH-GUIDE-STYLE') + assert.equal(await page.locator('.guide-project-title').count(), count, 'Search waits for explicit submission') + await page.locator('.guide-project-list').getByRole('button', { name:'搜索', exact:true }).click() + await expect(page.locator('.guide-project-title')).toHaveCount(0) + await page.getByRole('button', { name:'重置', exact:true }).click() + await expect(page.locator('.guide-project-title')).toHaveCount(count) + await page.locator('.system-pagination .btn-next').click() + await expect(page.locator('.guide-project-title')).toHaveCount(2) + await page.locator('.system-pagination .btn-prev').click() + await expect(page.locator('.guide-project-title')).toHaveCount(10) + if (count) { + await page.locator('.guide-project-actions').first().getByRole('button', { name:'更多' }).click() + await expect(page.getByRole('menuitem', { name:'项目详情', exact:true })).toBeVisible() + await shot('02-list-actions') + await page.getByRole('menuitem', { name:'项目详情', exact:true }).click() + await expect(page.getByRole('button', { name:'关闭项目详情' })).toBeVisible() + await page.getByRole('button', { name:'关闭项目详情' }).click() + await page.locator('.guide-project-actions').first().getByRole('button', { name:'进入编制', exact:true }).click() + await expect(page.locator('.ofd-view-editor')).toBeVisible({ timeout:60000 }) + await shot('03-editor') + await theme('dark') + await shot('03-editor-dark') + await theme('military') + await page.locator('.ofd-top-actions [data-action="open-preview"]').click() + await expect(page.locator('.ofd-workspace-dialog.preview')).toBeVisible() + await shot('03-guide-preview') + console.log('Editor loaded', page.url()) + } + await page.goto('http://127.0.0.1:6180/content/ofd/templates') + await expect(page.locator('.guide-tpl-card')).toHaveCount(4, { timeout:30000 }) + await page.locator('[data-guide-tpl-action="select"]').nth(1).click() + await expect(page.locator('.guide-tpl-card').nth(1)).toHaveClass(/is-active/) + await shot('04-templates') + await page.locator('.guide-tpl-card').nth(1).getByRole('button', { name:'预览', exact:true }).click() + await expect(page.locator('.guide-tpl-modal')).toBeVisible() + await shot('05-template-preview') + await page.goto('http://127.0.0.1:6180/content/ofd/api') + await expect(page.locator('.ofd-api-layout')).toBeVisible({ timeout:30000 }) + await shot('06-api') + await page.getByRole('button', { name:'执行调用', exact:true }).click() + await expect(page.locator('.ofd-api-result')).toBeVisible() + await expect(page.locator('.ofd-api-result header b')).toHaveText('200') + await shot('07-api-result') + for (const mode of ['technology','graphite','dark']) { + await theme(mode) + await shot(`08-api-${mode}`) + } + await page.setViewportSize({ width:1366, height:768 }) + await theme('military') + await shot('09-api-laptop') + await page.goto('http://127.0.0.1:6180/content/ofd/templates') + await expect(page.locator('.guide-tpl-card')).toHaveCount(4) + await shot('10-templates-laptop') + await theme('dark') + await shot('11-templates-dark') + await page.goto('http://127.0.0.1:6180/content/ofd') + await expect(page.locator('.guide-project-list')).toBeVisible() + await shot('12-list-dark') + await theme('military') + await page.getByRole('button', { name:'新建指导书', exact:true }).click() + await expect(page.locator('.ofd-app')).toBeVisible() + await shot('13-create-wizard') + assert.equal((await context.request.get('http://192.168.31.168:6180/')).status(),200) + assert.deepEqual(errors, [], 'No unhandled browser exceptions') + checks.push({ name:'搜索/重置、详情、编制入口、模板选择/预览、GET 接口自检、主题切换、局域网入口', result:'PASS' }) +} catch(error) { + await page.screenshot({ path:path.join(report,'failure.png') }).catch(()=>{}) + console.error('Browser errors:',errors) + throw error +} finally { + fs.writeFileSync(path.join(report,'results.json'), JSON.stringify({ checks, errors },null,2)) + await browser.close() +}