diff --git a/activities/part-substitution-assessment/__tests__/activity.test.ts b/activities/part-substitution-assessment/__tests__/activity.test.ts index 8462a9a..d3ca6b9 100644 --- a/activities/part-substitution-assessment/__tests__/activity.test.ts +++ b/activities/part-substitution-assessment/__tests__/activity.test.ts @@ -26,7 +26,12 @@ const ITEMS = ['S1_inputs', 'S2_assess', 'S3_review', 'T_release']; * 入库事实 / 分析结论一落地引擎就自己收步,不再需要人工激活等待。 */ const NO_DONE_WHEN = new Set(['S3_review', 'T_release']); -const VIEWS = ['p08-master-create', 'p10-document-create', 'eval-input-list', 'difference-review', 'landing-confirm']; +/* + * 2026-08-29 Task 7:五张旧局面收成三张 —— `p08-master-create` / `p10-document-create` / + * `eval-input-list` 三张合并成一张 `input-supplement`(Task 7 已交付); + * `difference-review` / `landing-confirm` 由 Task 8 / 9 各自重做(同名)。 + */ +const VIEWS = ['input-supplement', 'difference-review', 'landing-confirm']; /** 运营中心 L2 沙箱里那张关键台账数据看板 —— 不是工单件,只在「都声明了」这一条里出现;自己那组断言在文件尾 */ const BOARD_VIEW = 'activity-ledger'; const byAddress = (domain: string, object: string) => manifest.declarations.ledger.find((l) => l.domain === domain && l.object === object); @@ -99,12 +104,11 @@ describe('清单本身合规', () => { }); /* - * SKIP(2026-08-29,Task 2):`assets.viewTemplates` 已按 spec §8 暂时只留 `activity-ledger` - * 一张(五张旧局面全部作废,它们绑的是已退休的九步与旧台账地址)—— Task 7–9 会各自加回 - * `input-supplement` / `difference-review` / `landing-confirm` 三张新件,那时再补一条按新 - * 视图集合钉死的断言。这条转性检查在过渡期间的意义不大,先跳过、不删。 + * SKIP(2026-08-29,Task 2 → Task 7 部分交付):`input-supplement` 已随 Task 7 补回 + * `assets.viewTemplates`(`VIEWS` 已更新),但 `difference-review` / `landing-confirm` + * 两张还在等 Task 8 / 9 —— `new Set(...)` 的整体相等要三张都在才成立,继续跳过。 */ - it.skip('五张工单视图 + 一张运营侧看板都声明了,剧本每一步引用的 view 都在其中', () => { + it.skip('三张工单视图 + 一张运营侧看板都声明了,剧本每一步引用的 view 都在其中', () => { expect(new Set(manifest.assets.viewTemplates.map((v) => v.name))).toEqual(new Set([...VIEWS, BOARD_VIEW])); for (const step of manifest.assets.businessModel.steps as ReadonlyArray<{ view?: string }>) { if (step.view !== undefined) expect(VIEWS, step.view).toContain(step.view); @@ -315,10 +319,14 @@ describe('案例模型与清单的对齐', () => { describe('报告模版:编辑面与只读面各就各位', () => { /* - * SKIP(2026-08-29,Task 2 → Task 7 补回):`p08-master-create` / `p10-document-create` / - * `eval-input-list` 三张局面已随重写从 `assets.viewTemplates` 撤下(spec §8:五张旧件全部作废, - * 新的 `input-supplement` 由 Task 7 重做)。`viewHtml()` 现在对这三个名字一律返回空串, - * 下面几条对 HTML 结构的断言无从谈起 —— 跳过、不删,等 Task 7–9 补回视图再验。 + * SKIP(2026-08-29,Task 2 → Task 7,复核后维持 skip):`p08-master-create` / `p10-document-create` / + * `eval-input-list` 三张局面已被 Task 7 的 `input-supplement` 一张顶掉,但这条测的形状 + * 与新设计不同,不是简单换个 view 名字就能救活:①P-10(`parts.document`)整个不在新 + * S1 的范围里(本活动不再碰它);②新设计里 `parts.profile` 两行由系统铺、人不加行 + * (要建哪颗料是启动参数定死的事),`parts.parameter` 的行集由配方从 + * `sub_eval.part × parts.type_param` 派生且 `prune:true`(手加的孤行下一拍就被删)—— + * 这两张表都**不给** `data-edit-add`,只有 `parts.type_param`(新建类型的字段清单)才有。 + * 新覆盖见下面「件① 评估输入补全」;继续 skip、不改断言内容,留给读的人对照差异。 */ it.skip('P-08 数据工单件的表 1 + 表 2(标准化参数子表)都可加行可编辑:data-edit / data-edit-add 与主键 data-col 都在', () => { const p08 = viewHtml('p08-master-create'); @@ -350,11 +358,9 @@ describe('报告模版:编辑面与只读面各就各位', () => { } }); - it('每个可编辑格子的 data-col 都在对应台账对象的声明里(p08-master-create 一屏两张表,两张台账对象的声明并起来看)', () => { + it('每个可编辑格子的 data-col 都在对应台账对象的声明里(input-supplement 一屏三张表,三张台账对象的声明并起来看)', () => { const cases: Array<[string, Array<[string, string]>]> = [ - ['p08-master-create', [['parts', 'profile'], ['parts', 'parameter']]], - ['p10-document-create', [['parts', 'document']]], - ['eval-input-list', [[D, 'input_gap']]], + ['input-supplement', [['parts', 'profile'], ['parts', 'type_param'], ['parts', 'parameter']]], ]; for (const [view, objects] of cases) { const cols = new Set([...viewHtml(view).matchAll(/data-col="([^"]+)"/g)].map((m) => m[1] ?? '')); @@ -380,8 +386,13 @@ describe('报告模版:编辑面与只读面各就各位', () => { await expect(host.base.interaction.pushHtml('
')).rejects.toThrow(/无脚本执行权/); }); - /* SKIP(2026-08-29,Task 2 → Task 7-9):VIEWS 列的五张局面已全部撤下,`assets.viewTemplates` 现在只有运营侧看板一张 */ - it.skip('五张模版的外壳都是搬自目标件的 page/card,不再含 E04/I01 那套视觉底(.proto-root)的痕迹', () => { + /* + * SKIP(2026-08-29,Task 2 → Task 7 部分交付):VIEWS 现在是三张新局面的名单,`input-supplement` + * 已随 Task 7 补回、这一条本身可以对它验(外壳确实是 page/card),但 `difference-review` / + * `landing-confirm` 两张还没有(viewHtml 返回空串,`toContain('class="card-head"')` 会假), + * 循环体没法只验一部分 —— 等 Task 8/9 补齐三张再一起解封。 + */ + it.skip('三张模版的外壳都是搬自目标件的 page/card,不再含 E04/I01 那套视觉底(.proto-root)的痕迹', () => { for (const view of VIEWS) { const html = viewHtml(view); expect(html, view).toContain('class="card-head"'); @@ -391,6 +402,56 @@ describe('报告模版:编辑面与只读面各就各位', () => { }); }); +/* + * 件① 评估输入补全(2026-08-29,Task 7):S1 的配方缺什么就发一张数据工单,`view: 'input-supplement'` + * 指向它。三块竖排——要评估什么(两颗料的存在性 + 主数据)/ 芯片类型(选型 + 新建类型时的核心参数 + * 字段清单)/ 参数值(逐条实测/规格值);顶部一条红带摆配方算好的 `gate_reason`。 + * 复用文件顶部已有的 `viewHtml` 短写(与 brief 草稿里的 `viewOf` 同一件事,不重复定义一个)。 + */ +describe('件① 评估输入补全', () => { + const html = viewHtml('input-supplement'); + + it('在 viewTemplates 里,且 S1 指向它', () => { + expect(html).not.toBe(''); + const steps = manifest.assets.businessModel.steps as ReadonlyArray<{ item: string; view?: string }>; + expect(steps.find((s) => s.item === 'S1_inputs')?.view).toBe('input-supplement'); + }); + + it('三块都在,行都绑本单草稿(不是整表)', () => { + expect(html).toContain('data-rows="mine:sub_eval.part"'); + expect(html).toContain('data-rows="mine:parts.parameter"'); + expect(html).toContain('data-rows="mine:parts.type_param"'); + }); + + it('芯片类型是从台账现读的下拉 —— 写死等于「只能选出包那天就有的那几种」', () => { + expect(html).toContain('data-options="parts.chip_type"'); + expect(html).toContain('data-option-value="type_id"'); + expect(html).toContain('data-option-label="type_name"'); + /* data-options 与 data-slot 不能共存(渲染时 data-slot 会被剥掉)—— 当下值由 data-col 定 */ + expect(/data-options="[^"]*"[^>]*data-slot=/.test(html)).toBe(false); + }); + + it('**主键列必挂 `data-col`** —— 它不是编辑面,是行的钥匙(漏了会把人改的行错配到别行上)', () => { + expect(html).toContain('data-col="internal_pn"'); + expect(html).toContain('data-col="param_name"'); + }); + + it('可改的三张表各有一道编辑闸', () => { + for (const address of ['parts.profile', 'parts.type_param', 'parts.parameter']) { + expect(html).toContain(`data-edit="${address}"`); + } + }); + + it('模版不算不判:没有内联脚本,红带的显隐交给样式表', () => { + expect(html).not.toContain('