// 只读断言:直接加载现有组件方法,禁止发出真实业务请求。 const assert = require('assert/strict'); const fs = require('fs'); const path = require('path'); const vm = require('vm'); const root = path.resolve(__dirname, '..'); const folder = path.join(root, 'src/views/warehouseManagement/index'); const compiler = require(path.join(root, 'node_modules/vue-template-compiler')); function load(file, bindings = {}) { let source = fs.readFileSync(path.join(folder, file), 'utf8'); if (file.endsWith('.vue')) source = compiler.parseComponent(source).script.content; source = source.replace(/^import .*;$/gm, '').replace(/export default/, 'module.exports =').replace(/export (function|const)/g, '$1'); const context = { module: { exports: {} }, ...bindings }; vm.runInNewContext(source, context, { filename: file }); return { value: context.module.exports, context }; } const { context: utils } = load('dashboard-utils.js'); const { monthRange, cleanQuery, displayNumber, findPortalEntry } = utils; assert.equal(monthRange(new Date(2024, 1, 15)).endTime, '2024-02-29 23:59:59'); assert.equal(monthRange(new Date(2026, 11, 31)).startTime, '2026-12-01 00:00:00'); assert.equal(displayNumber(null), '—'); assert.equal(displayNumber(''), '—'); assert.equal(displayNumber(0), '0'); assert.equal(cleanQuery({ id: '9223372036854775806', status: 0, scope: false, empty: '', missing: null }).id, '9223372036854775806'); assert.equal(cleanQuery({ status: 0 }).status, '0'); assert.equal(cleanQuery({ scope: false }).scope, 'false'); const menus = [{ path: '/page-wt', children: [{ path: '/bpm', children: [{ path: 'todo', component: 'bpm/todo/index' }] }] }]; assert.equal(findPortalEntry(menus, n => /bpm\/todo/.test(n.component || '')), '/page-wt/bpm/todo'); assert.equal(findPortalEntry([{ path: '/page-eom', children: [{ path: '/purchasingManage/purchaseNeedManage', name: '采购' }] }], n => n.name === '采购'), '/page-eom/purchasingManage/purchaseNeedManage'); assert.equal(findPortalEntry([{ path: 'https://example.com', name: '外部' }], () => true), ''); assert.equal(findPortalEntry(menus, n => n.name === '无权限菜单'), ''); let calls = 0; const { context: screens } = load('dashboard-screens.js'); const { value: page } = load('dashboard.vue', { HomeOverview: {}, DashboardDetail: {}, DashboardActions: {}, ...utils, screens: screens.screens, dashboardAction: async () => { calls++; return [{ key: 'a', success: true }, { key: 'b', success: false }]; } }); for (const entry of ['index.vue', 'common.vue', 'yuxin.vue']) { const source = fs.readFileSync(path.join(folder, entry), 'utf8'); assert.match(source, /from ['"]\.\/dashboard\.vue['"]/); assert.doesNotMatch(source, /from ['"]\.\/index\.vue['"]/); } const open = { filters: { unit: '吨', categoryId: '1', stockBatchId: '2' }, view: 'home', route(view, filters) { this.target = view; this.nextFilters = filters; } }; page.methods.open.call(open, 'todo'); assert.equal(open.nextFilters.type, 'inventory'); page.methods.open.call(open, 'ioapp'); assert.equal(open.nextFilters.status, '0'); assert.equal(open.nextFilters.unit, ''); assert.equal(open.nextFilters.categoryId, undefined); const receipt = { receiveQueue: [{ id: '1' }, { id: '2' }], preparedReceipts: [], receiving: false, $route: { query: { inId: '1' } }, nextReceipt() {} }; page.methods.prepareReceipt.call(receipt, { sourceBizNo: 'R1' }); page.methods.prepareReceipt.call(receipt, { sourceBizNo: 'R1' }); assert.equal(receipt.preparedReceipts.length, 1, '旧表单重复事件不能准备到下一张申请'); const { value: overview } = load('components/HomeOverview.vue', { DashboardChart: {}, displayNumber }); const kpis = overview.computed.kpis.call({ summary: {}, unit: '吨' }); assert.equal(kpis.find(item => item.key === 'stock').target, 'stockLedger'); assert.equal(kpis.find(item => item.key === 'value').target, 'stockLedger'); const todos = overview.computed.todoCards.call({ summary: {} }); assert.equal(todos.find(item => item.key === 'in').target, 'stockManagement'); assert.equal(todos.find(item => item.key === 'out').target, 'outgoingManagement'); const legacy = { filters: { unit: '吨' }, $router: { push(location) { this.location = location; return Promise.resolve(); } } }; for (const [view, expectedPath] of Object.entries({ ledger: '/warehouseManagement/stockLedger', stockLedger: '/warehouseManagement/stockLedger', stockManagement: '/warehouseManagement/stockManagement', outgoingManagement: '/warehouseManagement/outgoingManagement' })) { page.methods.open.call(legacy, view); assert.equal(legacy.$router.location.path, expectedPath); assert.equal(legacy.$router.location.query.dashboard, undefined, '原系统入口不应携带首页看板标记'); } let navigation = 0; overview.methods.categorySelect.call({ $message: { info() {} }, $emit() { navigation++; } }, { id: 'other' }); assert.equal(navigation, 0, '其他分组不能作为具体物料下钻'); (async () => { const state = { receiving: false, preparedReceipts: [{ requestKey: 'a' }, { requestKey: 'b' }], receiveQueue: [], filters: {}, $confirm: async () => {}, route() {}, $refs: { actions: { showResults() {} } }, refresh() {}, $message: { error(message) { throw new Error(message); } } }; await Promise.all([page.methods.submitReceipts.call(state), page.methods.submitReceipts.call(state)]); assert.equal(calls, 1, '重复点击只能发出一次批量请求'); assert.equal(state.preparedReceipts.length, 1); assert.equal(state.preparedReceipts[0].requestKey, 'b', '失败申请重试保留原幂等键'); assert.equal(state.receiving, false); console.log('仓储看板业务断言通过:月份、空值、大整数身份、授权入口、待办范围、分组下钻、收货去重与失败重试。'); })().catch(error => { console.error(error); process.exitCode = 1; });