云
2 天以前 1b57bc924e68db3873bf24deee91a75fa014ea96
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
const fs = require('node:fs');
const path = require('node:path');
const ts = require('typescript');
const assert = require('node:assert/strict');
const { parse, compileTemplate } = require('vue/compiler-sfc');
const root = path.join(__dirname, '../src/views/dashboard/analytics');
function evaluate(source, mocks) {
  const js = ts.transpileModule(source, { compilerOptions: { module: ts.ModuleKind.CommonJS, target: ts.ScriptTarget.ES2022 } }).outputText;
  const module = { exports: {} };
  new Function('require', 'module', 'exports', 'defineOptions', 'defineProps', 'defineEmits', js)(
    (id) => mocks[id] || {}, module, module.exports, () => {}, () => ({ repairActiveCount: 0 }), () => () => {});
  return module.exports;
}
const vue = { computed: (getter) => ({ get value() { return getter(); } }), ref: (value) => ({ value }) };
async function scenario(codes, names, expected) {
  const store = { accessRoutes: [{ name: 'Parent', children: names.map((name) => ({ name })) }] };
  const home = evaluate(fs.readFileSync(path.join(root, 'use-home-access.ts'), 'utf8'), {
    vue, '@vben/stores': { useAccessStore: () => store },
    '@vben/access': { useAccess: () => ({ hasAccessByCodes: (requested) => requested.some((code) => codes.includes(code)) }) },
  });
  const calls = [];
  const mocks = { '../use-home-access': home, './use-home-access': home,
    'vue-router': { useRouter: () => ({ push: () => {} }) }, './data': { defaultSummary: {} } };
  for (const [id, method] of [
    ['#/api/mes/home', 'getHomeSummary'], ['#/api/mes/home', 'getProductionTrend'],
    ['#/api/mes/home', 'getWorkOrderStatusDistribution'], ['#/api/erp/purchase/order', 'getPurchaseOrderPage'],
    ['#/api/erp/sale/order', 'getSaleOrderPage'], ['#/api/mes/wm/materialstock', 'getMaterialStockPage'],
    ['#/api/bpm/task', 'getTaskTodoPage'],
  ]) {
    mocks[id] ||= {};
    mocks[id][method] = async () => { calls.push(method); return method.startsWith('getHome') ? {} : method.endsWith('Page') ? { total: 3 } : []; };
  }
  mocks['@vben/plugins/echarts'] = { useEcharts: () => ({ renderEcharts: () => {} }) };
  for (const file of ['index.vue', 'modules/biz-cards.vue', 'modules/production-trend.vue', 'modules/alert-panel.vue']) {
    const mounted = [];
    mocks.vue = { ...vue, onMounted: (fn) => mounted.push(fn) };
    const { descriptor } = parse(fs.readFileSync(path.join(root, file), 'utf8'));
    evaluate(descriptor.scriptSetup.content, mocks);
    mounted.forEach((fn) => fn());
  }
  await new Promise((resolve) => setImmediate(resolve));
  assert.deepEqual(calls.sort(), expected.sort());
  const access = home.useHomeAccess();
  store.accessRoutes = [{ name: 'Forbidden', meta: { menuVisibleWithForbidden: true }, children: [{ name: 'Child' }] }];
  assert.equal(access.canNavigate('Forbidden'), false);
  assert.equal(access.canNavigate('Child'), false);
}
(async () => {
  await scenario([], [], []);
  await scenario(['erp:purchase-order:query'], ['ErpPurchaseOrder'], ['getPurchaseOrderPage']);
  await scenario(['erp:purchase-order:query'], [], []);
  await scenario(['mes:home:query'], [], ['getHomeSummary', 'getProductionTrend', 'getWorkOrderStatusDistribution']);
  await scenario(['*:*:*'], ['ErpPurchaseOrder', 'ErpSaleOrder', 'MesWmMaterialStock', 'BpmTaskTodo'],
    ['getHomeSummary', 'getProductionTrend', 'getWorkOrderStatusDistribution', 'getPurchaseOrderPage', 'getSaleOrderPage', 'getMaterialStockPage', 'getTaskTodoPage']);
  for (const file of ['index.vue', ...fs.readdirSync(path.join(root, 'modules')).filter((f) => f.endsWith('.vue')).map((f) => `modules/${f}`)]) {
    const { descriptor, errors } = parse(fs.readFileSync(path.join(root, file), 'utf8'));
    assert.equal(errors.length, 0, file);
    const result = compileTemplate({ source: descriptor.template.content, filename: file, id: file });
    assert.equal(result.errors.length, 0, `${file}: ${result.errors}`);
  }
  console.log('PASS: 5 permission scenarios, forbidden routes, and all analytics Vue templates');
})();