const assert = require('node:assert/strict') const fs = require('node:fs') const { chromium } = require('/Users/macclaw/.cache/codex-runtimes/codex-primary-runtime/dependencies/node/node_modules/playwright') const directory = '/Volumes/Extreme SSD/yawan-storage/weknora/acceptance' // Visual-state verification only. Native copy/approval behavior is tested in Go. async function main() { const credentials = JSON.parse(fs.readFileSync(`${directory}/super-admin-login.json`, 'utf8')) const response = await fetch('http://127.0.0.1:18080/api/v1/auth/login', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ email: credentials.email, password: credentials.password }), }) assert.equal(response.status, 200, 'acceptance login') const auth = await response.json() const browser = await chromium.launch({ headless: true, channel: 'chrome' }) try { const context = await browser.newContext({ viewport: { width: 1440, height: 1000 } }) await context.addInitScript(a => { const entries = { weknora_user: JSON.stringify({ ...a.user, tenant_id: String(a.user.tenant_id) }), weknora_tenant: JSON.stringify({ ...a.active_tenant, id: '10059' }), weknora_token: a.token, weknora_refresh_token: a.refresh_token, weknora_memberships: JSON.stringify(a.memberships), 'weknora:new-user-guide-done:v1': '1', } for (const [key, value] of Object.entries(entries)) localStorage.setItem(key, value) }, auth) const base = { owner_id: auth.user.id, kind: 'knowledge', source_id: 'source', personal_id: 'personal', no_export: true, hidden: false, created_at: new Date().toISOString() } const forks = [ { ...base, id: 'copy', name: '教学资料与实操案例完整知识库个人版本', status: 'copying' }, { ...base, id: 'failed', name: '需要重新复制的资料', status: 'failed' }, ] const jobs = [ { id: 'job-copy', fork_id: 'copy', purpose: 'fork', status: 'running', processed: 35, total: 100 }, { id: 'job-failed', fork_id: 'failed', purpose: 'fork', status: 'failed', error: '资料复制失败,请重试。' }, { id: 'job-review', submission_id: 'review', purpose: 'submission', status: 'running', processed: 14, total: 20 }, ] const submissions = [{ ...base, id: 'review', fork_id: 'copy', owner_name: auth.user.username, name: '教学资料知识库', status: 'snapshotting', note: '更新分类与教学案例', base_snapshot: { description: '原说明' }, proposed_snapshot: { description: '新的说明', configuration: { indexing: 'vector' } } }] let releaseFirstDocument, failSecondDocument = false const docA = { id: 'doc-a', title: '第一份完整教学资料与实践案例说明文档', type: 'file' } const docB = { id: 'doc-b', title: '第二份文档:本次提交时的只读正文和分类信息', type: 'file' } await context.route('**/api/v1/resource-versions**', async route => { const url = new URL(route.request().url()) if (route.request().method() !== 'GET') return route.fulfill({ status: 405, json: { message: 'Visual test does not mutate resources' } }) if (url.pathname.endsWith('/knowledge')) { const id = url.searchParams.get('document_id') if (!id) return route.fulfill({ status: 200, json: { success: true, data: { page: 1, total: 2, documents: [docA, docB] } } }) if (id === docB.id && failSecondDocument) return route.fulfill({ status: 503, json: { message: '暂时无法读取提交正文,请重试' } }) const body = { success: true, data: { page: 1, total: 1, chunks: [{ id: id + '-chunk', chunk_index: 0, is_enabled: true, content: id === docA.id ? '第一份迟到的旧请求正文' : '第二份文档的完整提交正文。人工修改在提交时保存为独立快照,后续个人编辑不会改变这一段。\n\n文档中的段落与换行均完整保留。' }] } } if (id === docA.id && releaseFirstDocument === undefined) return new Promise(resolve => { releaseFirstDocument = async () => { await route.fulfill({ status: 200, json: body }); resolve() } }) return route.fulfill({ status: 200, json: body }) } const data = url.pathname.endsWith('/catalog') ? [{ kind: 'knowledge', id: 'source', name: '教学资料知识库', owner_name: '管理员', can_fork: true, hidden: false }] : { forks, submissions, jobs, history: [] } return route.fulfill({ status: 200, json: { success: true, data } }) }) const page = await context.newPage(), errors = [] page.on('pageerror', error => errors.push(error.message)) await page.goto('http://127.0.0.1:15173/platform/workbench/resource-versions') await page.getByRole('heading', { name: '我的资源版本' }).waitFor() await page.getByText('个人版本', { exact: true }).click() await page.getByText('已复制 35 / 100 份资料', { exact: true }).waitFor() assert.ok(await page.getByRole('button', { name: '重新复制', exact: true }).isVisible()) assert.ok(await page.getByRole('button', { name: '编辑知识库', exact: true }).first().isDisabled()) await page.screenshot({ path: `${directory}/resource-versions-desktop.png`, animations: 'disabled' }) await page.setViewportSize({ width: 390, height: 844 }) assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 2), 'mobile overflow') await page.screenshot({ path: `${directory}/resource-versions-mobile.png`, animations: 'disabled' }) await page.getByText('提交记录', { exact: true }).click() await page.getByText('已复制 14 / 20 份资料', { exact: true }).waitFor() await page.getByRole('button', { name: '查看对比', exact: true }).click() await page.getByText('版本对比', { exact: true }).waitFor() assert.equal(await page.getByRole('button', { name: '批准更新全局', exact: true }).count(), 0, 'unfinished snapshot cannot approve') await page.screenshot({ path: `${directory}/resource-version-review-mobile.png`, animations: 'disabled' }) submissions[0].status = 'pending' submissions[0].snapshot_id = 'stage' await page.reload() await page.getByRole('heading', { name: '我的资源版本' }).waitFor() await page.getByText('提交记录', { exact: true }).click() await page.getByRole('button', { name: '查看对比', exact: true }).click() await page.getByRole('heading', { name: '提交知识全文' }).waitFor() await page.getByRole('button', { name: docB.title, exact: true }).click() await page.getByText('第二份文档的完整提交正文。', { exact: false }).waitFor() assert.equal(typeof releaseFirstDocument, 'function') await releaseFirstDocument() assert.equal(await page.getByText('第一份迟到的旧请求正文', { exact: true }).count(), 0, 'stale document request overwrote active selection') assert.ok(await page.evaluate(() => document.documentElement.scrollWidth <= innerWidth + 2), 'snapshot mobile overflow') await page.screenshot({ path: `${directory}/resource-version-fulltext-mobile.png`, animations: 'disabled' }) await page.setViewportSize({ width: 1440, height: 1000 }) await page.screenshot({ path: `${directory}/resource-version-fulltext-desktop.png`, animations: 'disabled' }) failSecondDocument = true await page.getByRole('button', { name: docB.title, exact: true }).click() await page.getByText('暂时无法读取提交正文,请重试', { exact: true }).waitFor() failSecondDocument = false await page.getByRole('button', { name: '重试', exact: true }).click() await page.getByText('第一份迟到的旧请求正文', { exact: true }).waitFor() assert.deepEqual(errors, [], 'Vue runtime errors') console.log('Resource version visual states passed: desktop/mobile progress and full text, disabled editing, retry, immutable snapshot preparation, stale request isolation, error recovery, review drawer.') } finally { await browser.close() } } main().catch(error => { console.error(error.message); process.exitCode = 1 })