check-warehouse-dashboard.cjs 5.7 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283
  1. // 只读断言:直接加载现有组件方法,禁止发出真实业务请求。
  2. const assert = require('assert/strict');
  3. const fs = require('fs');
  4. const path = require('path');
  5. const vm = require('vm');
  6. const root = path.resolve(__dirname, '..');
  7. const folder = path.join(root, 'src/views/warehouseManagement/index');
  8. const compiler = require(path.join(root, 'node_modules/vue-template-compiler'));
  9. function load(file, bindings = {}) {
  10. let source = fs.readFileSync(path.join(folder, file), 'utf8');
  11. if (file.endsWith('.vue')) source = compiler.parseComponent(source).script.content;
  12. source = source.replace(/^import .*;$/gm, '').replace(/export default/, 'module.exports =').replace(/export (function|const)/g, '$1');
  13. const context = { module: { exports: {} }, ...bindings };
  14. vm.runInNewContext(source, context, { filename: file });
  15. return { value: context.module.exports, context };
  16. }
  17. const { context: utils } = load('dashboard-utils.js');
  18. const { monthRange, cleanQuery, displayNumber, findPortalEntry } = utils;
  19. assert.equal(monthRange(new Date(2024, 1, 15)).endTime, '2024-02-29 23:59:59');
  20. assert.equal(monthRange(new Date(2026, 11, 31)).startTime, '2026-12-01 00:00:00');
  21. assert.equal(displayNumber(null), '—');
  22. assert.equal(displayNumber(''), '—');
  23. assert.equal(displayNumber(0), '0');
  24. assert.equal(cleanQuery({ id: '9223372036854775806', status: 0, scope: false, empty: '', missing: null }).id, '9223372036854775806');
  25. assert.equal(cleanQuery({ status: 0 }).status, '0');
  26. assert.equal(cleanQuery({ scope: false }).scope, 'false');
  27. const menus = [{ path: '/page-wt', children: [{ path: '/bpm', children: [{ path: 'todo', component: 'bpm/todo/index' }] }] }];
  28. assert.equal(findPortalEntry(menus, n => /bpm\/todo/.test(n.component || '')), '/page-wt/bpm/todo');
  29. assert.equal(findPortalEntry([{ path: '/page-eom', children: [{ path: '/purchasingManage/purchaseNeedManage', name: '采购' }] }], n => n.name === '采购'), '/page-eom/purchasingManage/purchaseNeedManage');
  30. assert.equal(findPortalEntry([{ path: 'https://example.com', name: '外部' }], () => true), '');
  31. assert.equal(findPortalEntry(menus, n => n.name === '无权限菜单'), '');
  32. let calls = 0;
  33. const { context: screens } = load('dashboard-screens.js');
  34. const { value: page } = load('dashboard.vue', {
  35. HomeOverview: {}, DashboardDetail: {}, DashboardActions: {}, ...utils, screens: screens.screens,
  36. dashboardAction: async () => { calls++; return [{ key: 'a', success: true }, { key: 'b', success: false }]; }
  37. });
  38. for (const entry of ['index.vue', 'common.vue', 'yuxin.vue']) {
  39. const source = fs.readFileSync(path.join(folder, entry), 'utf8');
  40. assert.match(source, /from ['"]\.\/dashboard\.vue['"]/);
  41. assert.doesNotMatch(source, /from ['"]\.\/index\.vue['"]/);
  42. }
  43. const open = { filters: { unit: '吨', categoryId: '1', stockBatchId: '2' }, view: 'home', route(view, filters) { this.target = view; this.nextFilters = filters; } };
  44. page.methods.open.call(open, 'todo');
  45. assert.equal(open.nextFilters.type, 'inventory');
  46. page.methods.open.call(open, 'ioapp');
  47. assert.equal(open.nextFilters.status, '0');
  48. assert.equal(open.nextFilters.unit, '');
  49. assert.equal(open.nextFilters.categoryId, undefined);
  50. const receipt = { receiveQueue: [{ id: '1' }, { id: '2' }], preparedReceipts: [], receiving: false, $route: { query: { inId: '1' } }, nextReceipt() {} };
  51. page.methods.prepareReceipt.call(receipt, { sourceBizNo: 'R1' });
  52. page.methods.prepareReceipt.call(receipt, { sourceBizNo: 'R1' });
  53. assert.equal(receipt.preparedReceipts.length, 1, '旧表单重复事件不能准备到下一张申请');
  54. const { value: overview } = load('components/HomeOverview.vue', { DashboardChart: {}, displayNumber });
  55. const kpis = overview.computed.kpis.call({ summary: {}, unit: '吨' });
  56. assert.equal(kpis.find(item => item.key === 'stock').target, 'stockLedger');
  57. assert.equal(kpis.find(item => item.key === 'value').target, 'stockLedger');
  58. const todos = overview.computed.todoCards.call({ summary: {} });
  59. assert.equal(todos.find(item => item.key === 'in').target, 'stockManagement');
  60. assert.equal(todos.find(item => item.key === 'out').target, 'outgoingManagement');
  61. const legacy = { filters: { unit: '吨' }, $router: { push(location) { this.location = location; return Promise.resolve(); } } };
  62. for (const [view, expectedPath] of Object.entries({
  63. ledger: '/warehouseManagement/stockLedger',
  64. stockLedger: '/warehouseManagement/stockLedger',
  65. stockManagement: '/warehouseManagement/stockManagement',
  66. outgoingManagement: '/warehouseManagement/outgoingManagement'
  67. })) {
  68. page.methods.open.call(legacy, view);
  69. assert.equal(legacy.$router.location.path, expectedPath);
  70. assert.equal(legacy.$router.location.query.dashboard, undefined, '原系统入口不应携带首页看板标记');
  71. }
  72. let navigation = 0;
  73. overview.methods.categorySelect.call({ $message: { info() {} }, $emit() { navigation++; } }, { id: 'other' });
  74. assert.equal(navigation, 0, '其他分组不能作为具体物料下钻');
  75. (async () => {
  76. 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); } } };
  77. await Promise.all([page.methods.submitReceipts.call(state), page.methods.submitReceipts.call(state)]);
  78. assert.equal(calls, 1, '重复点击只能发出一次批量请求');
  79. assert.equal(state.preparedReceipts.length, 1);
  80. assert.equal(state.preparedReceipts[0].requestKey, 'b', '失败申请重试保留原幂等键');
  81. assert.equal(state.receiving, false);
  82. console.log('仓储看板业务断言通过:月份、空值、大整数身份、授权入口、待办范围、分组下钻、收货去重与失败重试。');
  83. })().catch(error => { console.error(error); process.exitCode = 1; });