/** * **多宿主收值 + 嵌套异地址排除**(2026-08-31,W05 芯片卡翻面逼出的平台扩面)。 * * 版面要求「参数值印在每张芯片卡的**背面**、就地可改」:参数表的行天然分居两张卡, * 一个宿主装不下 —— `hostsOf` 把同一地址的全部宿主都数进来,每个宿主各自归行。 * * 更要紧的是**嵌套异地址排除**:卡背的参数宿主(`data-edit="parts.parameter"`)住在 * 芯片卡宿主(`data-edit="parts.profile"`)的行里 —— 外层收值若把里层宿主的格子也 * 收走,参数值就会被当成主数据的列并进主数据的行(`view-container-collect-nested.test.ts` * 钉过的静默丢数据,换一副面孔回来)。规则:**格子只归它最近的 `data-edit` 祖先**。 * 嵌套的 `data-rows` 不带 `data-edit` 时行为原样(照旧并行 —— 那两条钉测不动)。 */ // @vitest-environment jsdom import { describe, expect, it } from 'vitest'; import { collect, mirrorTwins, setOpenIn } from '../src/ViewContainer.tsx'; /** 两张芯片卡:正面是主数据格(profile),背面各住一个参数宿主(parameter) */ const FLIP_CARDS = '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
' + '
'; function docOf(html: string): Document { const doc = document.implementation.createHTMLDocument(''); doc.body.innerHTML = html; return doc; } describe('多宿主 collect():同一地址的宿主都数进来,格子只归最近的 data-edit 祖先', () => { it('parts.parameter 分居两张卡背的两个宿主 —— 4 个瓷贴收到 4 行,主键各自带对', () => { const got = collect(docOf(FLIP_CARDS), 'parts.parameter'); expect(got).toHaveLength(4); const xl = got.filter((r) => r['internal_pn'] === 'XL2596S-5.0'); expect(xl).toHaveLength(2); expect(new Map(xl.map((r) => [r['param_name'], r['param_value']])).get('输出电流')).toBe('2.5'); }); it('parts.profile 收值**不含**卡背参数宿主的格子 —— 两行主数据,一列参数都不许混进来', () => { const got = collect(docOf(FLIP_CARDS), 'parts.profile'); expect(got).toHaveLength(2); for (const row of got) { expect(row['brand_name']).toBeDefined(); /* 静默丢数据的那副面孔:param_value 若出现在主数据行里,就是里层宿主被外层吞了 */ expect(row['param_value']).toBeUndefined(); expect(row['param_name']).toBeUndefined(); } }); it('setOpenIn:一把闸开合同一地址的**全部**宿主(非表格容器,.on 落在宿主自身)', () => { const doc = docOf(FLIP_CARDS); setOpenIn(doc, 'parts.parameter', true); const hosts = Array.from(doc.querySelectorAll('.pback')); expect(hosts).toHaveLength(2); expect(hosts.every((h) => h.classList.contains('on'))).toBe(true); expect(doc.querySelector('[data-edit-for="parts.parameter"]')?.classList.contains('on')).toBe(true); /* 外层 profile 宿主不许被顺手点亮 —— 两把闸各管各的 */ expect(doc.querySelector('[data-edit="parts.profile"]')?.classList.contains('on')).toBe(false); setOpenIn(doc, 'parts.parameter', false); expect(hosts.every((h) => !h.classList.contains('on'))).toBe(true); }); }); describe('mirrorTwins:下拉镜像的是选项文案,不是 value(2026-08-31「保存一瞬间变数字」)', () => { const SEL = '
' + '旧名' + '' + '
'; it('镜进孪生件的是「DC-DC 降压稳压器」,不是「1」;selected 属性拨到人选的那一项(守卫 CSS 认它)', () => { const doc = document.implementation.createHTMLDocument(''); doc.body.innerHTML = SEL; const sel = doc.querySelector('select') as HTMLSelectElement; sel.value = '1'; mirrorTwins(doc, 'parts.profile'); expect(doc.querySelector('.tsel')?.textContent).toBe('DC-DC 降压稳压器'); expect(doc.querySelector('option[value="1"]')?.hasAttribute('selected')).toBe(true); expect(doc.querySelector('option[value="2"]')?.hasAttribute('selected')).toBe(false); }); it('没选(空占位)= 没答:孪生件一个字不动', () => { const doc = document.implementation.createHTMLDocument(''); doc.body.innerHTML = SEL; mirrorTwins(doc, 'parts.profile'); expect(doc.querySelector('.tsel')?.textContent).toBe('旧名'); }); });