import assert from 'node:assert/strict' import test from 'node:test' import { readFileSync } from 'node:fs' import { compileFunction } from 'node:vm' import { reactive, ref, watch } from './vue.mjs' const source = readFileSync(new URL('../../frontend/src/views/EmailDetail/EmailDetail.js', import.meta.url), 'utf8') const createPage = compileFunction( source.replace(/^import .*$/gm, '').replace('export function usePage', 'function usePage').replace(/export \{[^}]+\}/g, '') + '\nreturn usePage()', ['ref', 'watch', 'onBeforeUnmount', 'useRoute', 'useRouter', 'useGroupStore', 'http', 'ElMessage', 'ElMessageBox', 'lang', 'Document', 'Back', 'Delete', 'Download'] ) const settle = () => new Promise(resolve => setImmediate(resolve)) const email = id => ({errorNo: 0, data: {id, subject: `mail-${id}`, to: '[{"EmailAddress":"test@example.com"}]', cc: '[]', attachments: []}}) function fixture(post, id = '15') { const route = reactive({params: {id}}) const requests = [] const navigations = [] let unmount const page = createPage(ref, watch, callback => { unmount = callback }, () => route, () => ({push: target => navigations.push(target)}), () => ({tag: ''}), {post: async (...args) => { requests.push(args); return post(...args) }}, {}, {}, {}, {}, {}, {}, {}) return {page, route, requests, navigations, unmount: () => unmount()} } test('邮件业务错误不渲染空数据,允许重试后读取正文与附件', async () => { let response = {errorNo: 500, errorMsg: '读取邮件失败', data: ''} const {page, requests} = fixture(async () => response) assert.equal(page.loading.value, true) await settle() assert.equal(page.loading.value, false) assert.equal(page.loadError.value, '读取邮件失败') assert.equal(page.detailData.value.id, undefined) page.handleDelete() assert.equal(requests.length, 1) response = email(15) response.data.attachments = [{Index: 0, Filename: '中文附件.txt'}] await page.loadDetail() assert.equal(page.loadError.value, '') assert.equal(page.detailData.value.subject, 'mail-15') assert.equal(page.detailData.value.attachments[0].Filename, '中文附件.txt') assert.equal(page.tos.value[0].EmailAddress, 'test@example.com') }) test('网络失败提供中文错误,返回操作明确进入邮件列表', async () => { const {page, navigations} = fixture(async () => { throw new Error('Network Error') }) await settle() assert.equal(page.loading.value, false) assert.equal(page.loadError.value, '网络连接失败,请稍后重试') page.backToList() assert.equal(navigations[0].name, 'list') }) test('无效编号不发请求,空响应或编号不匹配不当作成功', async () => { for (const id of ['abc', '15abc', '-1', '0', '9007199254740992']) { const {page, requests} = fixture(async () => email(15), id) await settle() assert.equal(requests.length, 0) assert.equal(page.loadError.value, '邮件地址无效,请返回邮件列表') } for (const data of [null, '', [], {}, {id: 14}]) { const {page} = fixture(async () => ({errorNo: 0, data})) await settle() assert.equal(page.loadError.value, '邮件不可用,请返回邮件列表') assert.equal(page.detailData.value.id, undefined) } }) test('异常地址和附件字段不导致页面异常,切换邮件清除旧收件人', async () => { const {page, route} = fixture(async (_, {id}) => ({errorNo: 0, data: {id, to: id === 15 ? '[{"EmailAddress":"old@example.com"}]' : 'null', cc: id === 15 ? '[{"EmailAddress":"copy@example.com"}]' : '{', attachments: 'bad'}})) await settle() assert.equal(page.showCC.value, true) assert.equal(page.detailData.value.attachments.length, 0) route.params.id = '16' await settle() assert.equal(page.detailData.value.id, 16) assert.equal(page.tos.value.length, 0) assert.equal(page.ccs.value.length, 0) assert.equal(page.showCC.value, false) }) test('仅发件副本展示密送名单,切换到收件副本立即清除', async () => { const addresses = '[{"Name":"","EmailAddress":"hidden@example.com"}]' const {page, route} = fixture(async (_, {id}) => ({ errorNo: 0, data: {...email(id).data, type: id === 15 ? 1 : 0, bcc: addresses} })) await settle() assert.equal(page.showBCC.value, true) assert.equal(page.bccs.value[0].EmailAddress, 'hidden@example.com') route.params.id = '16' await settle() assert.equal(page.showBCC.value, false) assert.deepEqual(page.bccs.value, []) }) test('未知邮件类型和无效密送字段不展示名单,加载失败不残留旧名单', async () => { for (const data of [ {bcc: '[{"EmailAddress":"hidden@example.com"}]'}, {type: 1, bcc: '{'}, {type: 1, bcc: 'null'}, {type: 1, bcc: '[]'} ]) { const {page} = fixture(async () => ({errorNo: 0, data: {...email(15).data, ...data}})) await settle() assert.equal(page.showBCC.value, false) assert.deepEqual(page.bccs.value, []) } const {page, route} = fixture(async (_, {id}) => id === 15 ? {errorNo: 0, data: {...email(id).data, type: 1, bcc: '[{"EmailAddress":"hidden@example.com"}]'}} : {errorNo: 403, errorMsg: '无权查看邮件'}) await settle() assert.equal(page.showBCC.value, true) route.params.id = '16' await settle() assert.equal(page.showBCC.value, false) assert.deepEqual(page.bccs.value, []) }) test('切换邮件后旧请求成功或失败均不能覆盖新邮件', async () => { for (const staleFailure of [false, true]) { let finish let fail const {page, route} = fixture((_, {id}) => id === 15 ? new Promise((resolve, reject) => { finish = resolve; fail = reject }) : Promise.resolve(email(id))) route.params.id = '16' await settle() assert.equal(page.detailData.value.id, 16) if (staleFailure) fail(new Error('Network Error')) else finish(email(15)) await settle() assert.equal(page.detailData.value.id, 16) assert.equal(page.loadError.value, '') assert.equal(page.loading.value, false) } }) test('离开详情后未完成请求不再写入状态', async () => { let finish const {page, unmount} = fixture(() => new Promise(resolve => { finish = resolve })) unmount() finish(email(15)) await settle() assert.equal(page.detailData.value.id, undefined) })