Archived
harness 与工作台同日修:数据工单确认即入库 · 叫了人不再自动推进(两道结构闸)· 审核卡 = 终局验收卡结构 · 验收清单人点达标 · 迟到守卫不丢放行裁决 · 画面跟着这一拍发出去的卡走 · 成果详情只翻页 · 占位居中 · 进度条
human-tool / ledger / loop-engine / release / task / interaction 六模块与 app 层(desk 路由、engines、consumers、 data-order-landing、case-stamp、reset-runs、seed-activities、容器与 server 装配),web 的 desk-task (ViewRegion / chat-bubbles / review-face)与 app.css、ui-kit 图标;契约 card / manifest; 迁移 0034_task_seq 与迁移日志;api-report 快照;CLAUDE.md 快照、debt、活动开发规范、HTML 报告规范同步。 逐条口径见 CLAUDE.md 快照(2026-08-27 下午至晚)。另一会话做的,一直留在工作树没入库;与同批另两条提交互相依赖。 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_0167qWjeyBwvfdGovd7FWbCu
This commit is contained in:
@@ -374,3 +374,64 @@ describe('data-file-of:这一格点开是一份资料文件', () => {
|
||||
expect(out).not.toContain('onclick="x"');
|
||||
});
|
||||
});
|
||||
|
||||
/*
|
||||
* 嵌套行模版 + `data-by` 父键过滤(2026-08-27,W05 差异评估结论审核「一个候选一张表」逼出来的):
|
||||
* 外层按候选逐个复制,里层只列该候选名下的明细 —— 从前渲染器只认一层,里层的 data-rows 原样漏到成品里。
|
||||
*/
|
||||
describe('嵌套行模版与 data-by —— 一个候选一张表', () => {
|
||||
// 外层容器用 <article>:行模版是 <section>,里面不会再出现 <article>(行容器的标签名不得在行模版内部再出现)
|
||||
const html =
|
||||
'<article data-rows="rel"><section><h2 data-slot="pn"></h2>' +
|
||||
'<tbody data-rows="items" data-by="rel_id"><tr><td data-slot="name"></td><td data-slot="pn"></td></tr></tbody>' +
|
||||
'</section></article>';
|
||||
const scope = {
|
||||
rel: [
|
||||
{ rel_id: 'R1', pn: 'XL' },
|
||||
{ rel_id: 'R2', pn: 'AP' },
|
||||
],
|
||||
items: [
|
||||
{ rel_id: 'R1', name: '输出电压' },
|
||||
{ rel_id: 'R2', name: '使能逻辑' },
|
||||
{ rel_id: 'R1', name: '工作温度' },
|
||||
],
|
||||
};
|
||||
|
||||
it('里层只留与外层同键的行;父行的槽位仍填父值;data-by 属性随之消费', () => {
|
||||
const out = renderView(html, scope);
|
||||
const [first = '', second = ''] = out.split('</section>');
|
||||
expect(out.split('<section>').length - 1).toBe(2); // 一个候选一个 section
|
||||
expect(first).toContain('<h2>XL</h2>');
|
||||
expect(first).toContain('<td>输出电压</td>');
|
||||
expect(first).toContain('<td>工作温度</td>');
|
||||
expect(first).not.toContain('使能逻辑');
|
||||
expect(second).toContain('<h2>AP</h2>');
|
||||
expect(second).toContain('<td>使能逻辑</td>');
|
||||
expect(second).not.toContain('输出电压');
|
||||
expect(out).not.toContain('data-by');
|
||||
expect(out).not.toContain('data-rows');
|
||||
});
|
||||
|
||||
it('里层槽位与父行同名时取里层那一行(先展开里层,再填父行)', () => {
|
||||
const out = renderView(html, { ...scope, items: [{ rel_id: 'R1', name: 'n', pn: '子' }] });
|
||||
expect(out).toContain('<h2>XL</h2>');
|
||||
expect(out).toContain('<td>子</td>');
|
||||
});
|
||||
|
||||
it('data-by="子列=父列":两边列名不同也对得上 —— 当查表用(整表里挑这一颗料的那一行)', () => {
|
||||
const out = renderView(
|
||||
'<ul data-rows="rel"><li><b data-slot="pn"></b><span data-rows="profile" data-by="internal_pn=pn"><i data-slot="brand"></i></span></li></ul>',
|
||||
{ rel: [{ pn: 'XL' }, { pn: 'AP' }], profile: [{ internal_pn: 'AP', brand: 'Diodes' }, { internal_pn: 'XL', brand: 'XLSEMI' }] },
|
||||
);
|
||||
expect(out).toContain('<b>XL</b><span><i>XLSEMI</i></span>');
|
||||
expect(out).toContain('<b>AP</b><span><i>Diodes</i></span>');
|
||||
});
|
||||
|
||||
it('父值缺席一行不留;没有 data-by 的里层照旧全复制', () => {
|
||||
const none = renderView(html, { ...scope, rel: [{ rel_id: '', pn: 'X' }] });
|
||||
expect(none).toContain('<h2>X</h2>');
|
||||
expect(none).not.toContain('<td>');
|
||||
const all = renderView(html.replace(' data-by="rel_id"', ''), scope);
|
||||
expect(all.split('使能逻辑').length - 1).toBe(2);
|
||||
});
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user