Archived
feat(w05): 五张报告模版 —— 两张数据工单件、评估输入清单、差异审核件、落账确认件(目标件平移)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FJcX8A6TWp4GRs41TYryEh
This commit is contained in:
@@ -2,12 +2,11 @@
|
||||
* ACT-WORK-05 差异评估的包内自测 —— **只准 import @sdk / @contracts / @sfeel**。
|
||||
* 案例活动没有 `run`,自测不跑回合;测的是清单本身与授权面对齐,结构照 E04 / I01。
|
||||
*
|
||||
* 本文件目前只写三组(清单本身合规 / 台账声明 / 活动入口);
|
||||
* 「案例模型与清单的对齐」「报告模版:编辑面与只读面各就各位」两组留给 Task 4/5 追加 ——
|
||||
* `src/case-model.ts` 与 `src/views/report.ts` 眼下还是占位,写了那两组只会红。
|
||||
* 五组:清单本身合规 / 台账声明 / 案例模型与清单的对齐 / 报告模版:编辑面与只读面各就各位 / 活动入口。
|
||||
*/
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { activityManifestSchema } from '@contracts';
|
||||
import { createMockHost } from '@sdk';
|
||||
import { manifest, D, RULE_VERSION } from '../src/manifest.ts';
|
||||
import activity from '../src/index.ts';
|
||||
|
||||
@@ -15,6 +14,7 @@ const ITEMS = ['S0_bind', 'S0d_master_data', 'S1_input_confirm', 'S2_normalize',
|
||||
const NO_DONE_WHEN = new Set(['S0d_master_data', 'S1_input_confirm', 'S4_review', 'T_release']);
|
||||
const VIEWS = ['p08-master-create', 'p10-document-create', 'eval-input-list', 'difference-review', 'landing-confirm'];
|
||||
const byAddress = (domain: string, object: string) => manifest.declarations.ledger.find((l) => l.domain === domain && l.object === object);
|
||||
const viewHtml = (name: string) => manifest.assets.viewTemplates.find((t) => t.name === name)?.html ?? '';
|
||||
|
||||
describe('清单本身合规', () => {
|
||||
it('过 @contracts 的 schema', () => {
|
||||
@@ -177,8 +177,69 @@ describe('案例模型与清单的对齐', () => {
|
||||
});
|
||||
});
|
||||
|
||||
describe('报告模版:编辑面与只读面各就各位', () => {
|
||||
it('两张数据工单件可加行可编辑:data-edit / data-edit-add 与主键 data-col 都在', () => {
|
||||
const p08 = viewHtml('p08-master-create');
|
||||
expect(p08).toContain('data-edit="parts.profile"');
|
||||
expect(p08).toContain('data-edit-add="parts.profile"');
|
||||
expect(p08).toContain('data-col="internal_pn"');
|
||||
expect(p08).toContain('data-col="normalized_parameter_set"');
|
||||
const p10 = viewHtml('p10-document-create');
|
||||
expect(p10).toContain('data-edit="parts.document"');
|
||||
expect(p10).toContain('data-col="doc_seq"');
|
||||
});
|
||||
|
||||
it('评估输入清单:缺口表可编辑(supplement),绑定区只读', () => {
|
||||
const html = viewHtml('eval-input-list');
|
||||
expect(html).toContain(`data-edit="${D}.input_gap"`);
|
||||
expect(html).toContain('data-col="supplement"');
|
||||
expect(html).toContain(`data-rows="mine:${D}.pn_check"`);
|
||||
});
|
||||
|
||||
it('差异审核与落账确认只读:没有任何 data-edit / data-col', () => {
|
||||
for (const name of ['difference-review', 'landing-confirm']) {
|
||||
expect(viewHtml(name), name).not.toContain('data-edit=');
|
||||
expect(viewHtml(name), name).not.toContain('data-col=');
|
||||
}
|
||||
});
|
||||
|
||||
it('每个可编辑格子的 data-col 都在对应台账对象的声明里', () => {
|
||||
const cases: Array<[string, string, string]> = [
|
||||
['p08-master-create', 'parts', 'profile'],
|
||||
['p10-document-create', 'parts', 'document'],
|
||||
['eval-input-list', D, 'input_gap'],
|
||||
];
|
||||
for (const [view, domain, object] of cases) {
|
||||
const cols = new Set([...viewHtml(view).matchAll(/data-col="([^"]+)"/g)].map((m) => m[1] ?? ''));
|
||||
const declared = new Set(byAddress(domain, object)?.fields.map((f) => f.name));
|
||||
for (const c of cols) expect(declared.has(c), `${view}: ${c}`).toBe(true);
|
||||
}
|
||||
});
|
||||
|
||||
it('枚举列只用 data-class-of 投影,不摆 ASCII 原值', () => {
|
||||
expect(viewHtml('difference-review')).toContain('data-class-of="difference_status"');
|
||||
expect(viewHtml('difference-review')).toContain('data-class-of="recommendation_level"');
|
||||
expect(viewHtml('difference-review')).not.toContain('data-slot="difference_status"');
|
||||
});
|
||||
|
||||
it('宿主拒推含脚本的模版', async () => {
|
||||
const host = createMockHost({ contract: CONTRACT });
|
||||
await expect(host.base.interaction.pushHtml('<div><script>x()</script></div>')).rejects.toThrow(/无脚本执行权/);
|
||||
});
|
||||
});
|
||||
|
||||
describe('活动入口', () => {
|
||||
it('没有 run —— 行为全在清单里', () => {
|
||||
expect(activity.run).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
const CONTRACT = {
|
||||
activityId: 'part-substitution-assessment',
|
||||
version: '1.0.0',
|
||||
data: {},
|
||||
message: undefined,
|
||||
outputPorts: { mandatory: ['assessment-released'], selective: [] },
|
||||
prohibitions: [],
|
||||
discretion: [],
|
||||
} as const;
|
||||
|
||||
Reference in New Issue
Block a user