const assert = require('node:assert/strict') const fs = require('node:fs') const path = require('node:path') const test = require('node:test') const ts = require('typescript') const vue = require('vue') const { parse, compileScript, compileTemplate } = require('@vue/compiler-sfc') const root = path.resolve(__dirname, '..') const apiPath = path.join(root, 'src/api/setting/prescription_ai_visibility.ts') const pagePath = path.join(root, 'src/views/setting/prescription_ai_visibility/index.vue') const { descriptor, errors } = parse(fs.readFileSync(pagePath, 'utf8'), { filename: pagePath }) assert.deepEqual(errors, []) const script = compileScript(descriptor, { id: 'prescription-ai-visibility' }) function moduleFrom(source, dependencies) { const compiled = ts.transpileModule(source, { compilerOptions: { target: ts.ScriptTarget.ES2022, module: ts.ModuleKind.CommonJS, esModuleInterop: true } }).outputText const module = { exports: {} } new Function('require', 'module', 'exports', compiled)(dependencies, module, module.exports) return module.exports } const makeApi = (request) => moduleFrom(fs.readFileSync(apiPath, 'utf8'), (name) => { assert.equal(name, '@/utils/request') return request }) const tick = () => new Promise((resolve) => setImmediate(resolve)) const deferred = () => { let resolve let reject const promise = new Promise((yes, no) => { resolve = yes reject = no }) return { promise, resolve, reject } } function makePage(overrides = {}) { const saved = [] const messages = [] const calls = [] const api = { getPrescriptionAiCapabilities: async () => { calls.push('capabilities') return { agreement_visible: false, can_manage_visibility: true } }, getPrescriptionAiVisibilityConfig: async () => { calls.push('config') return { role_ids: [2, 90], admin_ids: [3, 91] } }, getPrescriptionAiVisibilityOptions: async () => { calls.push('options') return { roles: [{ id: 2, name: '医生' }], admins: [{ id: 3, name: '张医生', account: 'zhang.doctor' }] } }, setPrescriptionAiVisibilityConfig: async (params) => { saved.push(params) return params }, ...overrides } const component = moduleFrom(script.content, (name) => { if (name === 'vue') return vue if (name === '@/api/setting/prescription_ai_visibility') return api if (name === '@/utils/feedback') { return { msgSuccess: (message) => messages.push(message) } } throw new Error(`Unexpected import: ${name}`) }).default const page = component.setup({}, { expose() {} }) return { page, api, saved, messages, calls } } test('API uses the dedicated routes and rejects malformed policy responses', async () => { const calls = [] let response = { role_ids: [2], admin_ids: [3] } const api = makeApi({ get: async (params) => { calls.push(['get', params]) return response }, post: async (params) => { calls.push(['post', params]) return response } }) const data = await api.getPrescriptionAiVisibilityConfig() data.role_ids.push(5) assert.deepEqual(response.role_ids, [2]) await api.setPrescriptionAiVisibilityConfig({ role_ids: [], admin_ids: [] }) await api.getPrescriptionAiVisibilityOptions() await api.getPrescriptionAiCapabilities() assert.deepEqual(calls, [ ['get', { url: '/tcm.prescriptionAi/getVisibilityConfig' }], [ 'post', { url: '/tcm.prescriptionAi/setVisibilityConfig', params: { role_ids: [], admin_ids: [] } } ], ['get', { url: '/tcm.prescriptionAi/visibilityOptions' }], ['get', { url: '/tcm.prescriptionAi/capabilities' }] ]) for (response of [ null, {}, { role_ids: [], admin_ids: ['3'] }, { role_ids: [-1], admin_ids: [] } ]) { await assert.rejects(api.getPrescriptionAiVisibilityConfig(), /响应格式不正确/) await assert.rejects( api.setPrescriptionAiVisibilityConfig({ role_ids: [], admin_ids: [] }), /响应格式不正确/ ) } }) test('initial loading is pending and cannot submit before the real policy is read', async () => { const pending = deferred() const { page, saved } = makePage({ getPrescriptionAiVisibilityConfig: () => pending.promise }) assert.equal(page.loading.value, true) assert.equal(page.ready.value, false) assert.equal(page.hasLoaded.value, false) await page.handleSubmit() assert.deepEqual(saved, []) pending.reject(new Error('offline')) await tick() assert.equal(page.loading.value, false) assert.equal(page.ready.value, false) assert.match(page.loadError.value, /加载失败/) await page.handleSubmit() assert.deepEqual(saved, []) }) test('a failed reload cannot apply a partial policy or overwrite saved selections', async () => { const { page, api, saved } = makePage() await tick() api.getPrescriptionAiVisibilityConfig = async () => ({ role_ids: [], admin_ids: [] }) api.getPrescriptionAiVisibilityOptions = async () => { throw new Error('options failed') } await page.loadConfig() assert.deepEqual(page.formData.role_ids, [2, 90]) assert.deepEqual(page.formData.admin_ids, [3, 91]) assert.equal(page.hasLoaded.value, true) assert.equal(page.ready.value, false) await page.handleSubmit() assert.deepEqual(saved, []) api.getPrescriptionAiVisibilityOptions = async () => ({ roles: [], admins: [] }) await page.loadConfig() assert.equal(page.ready.value, true) assert.equal(page.loadError.value, '') assert.deepEqual(page.formData.role_ids, []) }) test('missing selected IDs stay visible and user labels support both name and account search', async () => { const { page, saved } = makePage() await tick() assert.equal(page.canManage.value, true) assert.equal(page.ready.value, true) assert.deepEqual( page.roleOptions.value.map((row) => row.id), [2, 90] ) assert.match(page.roleOptions.value[1].name, /#90.*当前不在可选列表/) assert.deepEqual( page.adminOptions.value.map((row) => row.id), [3, 91] ) assert.match(page.adminOptions.value[0].label, /张医生.*zhang\.doctor/) assert.match(page.adminOptions.value[1].label, /#91.*当前不在可选列表/) await page.handleSubmit() assert.deepEqual(saved, [{ role_ids: [2, 90], admin_ids: [3, 91] }]) }) test('explicitly clearing both selectors saves the empty policy and applies normalized response', async () => { const { page, saved, messages, api } = makePage() await tick() page.formData.role_ids = [] page.formData.admin_ids = [] await page.handleSubmit() assert.deepEqual(saved, [{ role_ids: [], admin_ids: [] }]) assert.equal(messages.length, 1) page.formData.role_ids = [90, 2, 90] api.setPrescriptionAiVisibilityConfig = async () => ({ role_ids: [2, 90], admin_ids: [] }) await page.handleSubmit() assert.deepEqual(page.formData.role_ids, [2, 90]) assert.equal(page.saving.value, false) }) test('pending save blocks duplicate writes and reload; failed write keeps the user selection', async () => { const pending = deferred() const saved = [] const { page, calls } = makePage({ setPrescriptionAiVisibilityConfig: (params) => { saved.push(params) return pending.promise } }) await tick() page.formData.role_ids = [90] const first = page.handleSubmit() assert.equal(page.saving.value, true) await page.handleSubmit() await page.loadConfig() assert.equal(saved.length, 1) assert.equal(calls.filter((name) => name === 'capabilities').length, 1) pending.reject(new Error('offline')) await first assert.equal(page.saving.value, false) assert.equal(page.ready.value, true) assert.deepEqual(page.formData.role_ids, [90]) assert.deepEqual(page.formData.admin_ids, [3, 91]) assert.match(page.saveError.value, /当前选择已保留/) }) test('management capability is independent from viewing and denies policy reads when absent', async () => { const { page, calls, saved } = makePage({ getPrescriptionAiCapabilities: async () => ({ agreement_visible: true, can_manage_visibility: false }) }) await tick() assert.deepEqual(calls, []) assert.equal(page.canManage.value, false) assert.equal(page.hasLoaded.value, false) assert.equal(page.ready.value, false) assert.match(page.loadError.value, /无权管理/) await page.handleSubmit() assert.deepEqual(saved, []) }) test('actual settings template compiles with its script bindings', () => { const compiled = compileTemplate({ source: descriptor.template.content, filename: pagePath, id: 'prescription-ai-visibility', compilerOptions: { bindingMetadata: script.bindings } }) assert.deepEqual(compiled.errors, []) })