import assert from 'node:assert/strict' import test from 'node:test' import { readFile } from 'node:fs/promises' import ts from 'typescript' const root = new URL('../../', import.meta.url) const read = (path) => readFile(new URL(path, root), 'utf8') test('危化品证上传位于运输管理并复用现有 WebView 壳', async () => { const source = await read('miniprogram/config/workbench-menus.js') const { WORKBENCH_MODULES } = await import(`data:text/javascript,${encodeURIComponent(source)}`) const transport = WORKBENCH_MODULES.find((m) => m.key === 'pl') const entry = transport.menus.find((m) => m.code === 'pl-plan-cert-webview') assert.ok(entry, '运输管理缺少危化品证上传入口') assert.equal(entry.name, '危化品证上传') assert.equal(entry.perms, 'mini:pl:plan-cert:list') assert.equal(entry.enabled, true) const api = await read('miniprogram/config/api.js') const ast = ts.createSourceFile('api.js', api, ts.ScriptTarget.Latest, true) let expression function visit(node) { if (ts.isPropertyAssignment(node) && node.name.getText(ast) === "'pl-plan-cert-webview'") expression = node.initializer.getText(ast) ts.forEachChild(node, visit) } visit(ast) assert.ok(expression, '缺少工作台路由映射') const path = new Function('PAGE_PATHS', `return ${expression}`)({ mpWebview: '/pages/common/mp-webview' }) assert.equal(path, '/pages/common/mp-webview?path=%2Fm%2Fpl%2Fplan-cert') }) test('H5 列表与详情均使用独立移动路由并隐藏桌面工作标签', async () => { const source = await read('web-admin/src/router/routes/staticRoutes.ts') const ast = ts.createSourceFile('routes.ts', source, ts.ScriptTarget.Latest, true) const routes = new Map() function visit(node) { if (ts.isObjectLiteralExpression(node)) { const pathProp = node.properties.find( (prop) => ts.isPropertyAssignment(prop) && prop.name.getText(ast) === 'path' ) if (pathProp && ts.isPropertyAssignment(pathProp) && ts.isStringLiteral(pathProp.initializer)) routes.set(pathProp.initializer.text, node) } ts.forEachChild(node, visit) } visit(ast) const listRoute = routes.get('/m/pl/plan-cert') const detailRoute = routes.get('/m/pl/plan-cert/:id') assert.ok(listRoute, '缺少 /m/pl/plan-cert 路由') assert.match(listRoute.getText(ast), /views\/m\/pl\/plan-cert\/index\.vue/) assert.ok(detailRoute, '缺少 /m/pl/plan-cert/:id 独立详情路由') assert.match(detailRoute.getText(ast), /views\/m\/pl\/plan-cert\/detail\.vue/) for (const route of [listRoute, detailRoute]) { assert.match(route.getText(ast), /isMpWebview:\s*true/) assert.match(route.getText(ast), /isHideTab:\s*true/) } }) test('菜单 SQL 仅补移动入口与已有授权角色的完整祖先链', async () => { const sql = await read('docs/sql/migrations/2026-10-17-mini-pl-plan-cert-webview-menus.sql') assert.match(sql, /START TRANSACTION/i) assert.match(sql, /COMMIT/i) assert.match(sql, /mini:pl:plan-cert:list/) assert.match(sql, /MINI_PL_ROOT/) assert.match(sql, /parent_id\s*=\s*'1'/) assert.match(sql, /EXISTS\s*\([\s\S]*m\.perms\s*=\s*'pl:plan-cert:list'[\s\S]*\)/) assert.match(sql, /r\.tenant_no\s*=\s*'shanxia2.0'/) assert.match(sql, /ON DUPLICATE KEY UPDATE/i) assert.doesNotMatch(sql, /\b(?:DELETE|DROP|ALTER|TRUNCATE)\s/i) assert.doesNotMatch(sql, /pl:plan-cert:(?:upload|ready)/) })