/*** 作用:@ui-arch/frame 内核冒烟——信号 / 副作用 / 计算值的语义断言(纯 JS,无需 DOM;跑 npm run smoke 执行) ***/ import { computed, effect, signal } from '../dist/signal.js'; let pass = 0; let fail = 0; /*** 断言:失败也继续跑完,最后统一给退出码(否则第一个红就看不到后面) ***/ const ok = (label, cond) => { if (cond) { pass += 1; console.log(`ok - ${label}`); return; } fail += 1; console.error(`NOT OK - ${label}`); }; // —— 1. 信号的读写与同值短路 —— const count = signal(0); let hits = 0; effect(() => { count(); hits += 1; }); ok('effect 创建时立即执行一次', hits === 1); count(1); ok('写值后重跑', hits === 2); count(1); ok('写入相同值不触发(Object.is 短路)', hits === 2); // —— 2. 注销后不再重跑 —— const live = signal('a'); let liveHits = 0; const dispose = effect(() => { live(); liveHits += 1; }); live('b'); ok('注销前正常重跑', liveHits === 2); dispose(); live('c'); ok('注销后写值不再重跑(dispose 必须真的摘订阅)', liveHits === 2); // —— 3. 条件分支重追踪:上一轮读过、这一轮不读的信号不该再唤它 —— const flag = signal(true); const left = signal(0); const right = signal(0); let branchHits = 0; effect(() => { branchHits += 1; if (flag()) left(); else right(); }); const afterInit = branchHits; flag(false); ok('切分支本身触发一次重跑', branchHits === afterInit + 1); const afterSwitch = branchHits; left(9); ok('切走后旧的依赖不再唤它(重追踪要清旧订阅)', branchHits === afterSwitch); right(9); ok('切走后新依赖仍唤它', branchHits === afterSwitch + 1); // —— 4. computed:派生、链式、同值短路 —— const price = signal(10); const qty = signal(2); const total = computed(() => price() * qty()); ok('computed 首次读即得派生值', total() === 20); price(3); ok('源变化后 computed 立即跟着变', total() === 6); let derivedHits = 0; effect(() => { total(); derivedHits += 1; }); const derivedBase = derivedHits; qty(3); ok('computed 变更会唤上层 effect(链式依赖成立)', derivedHits === derivedBase + 1); ok('链式计算值正确', total() === 9); // —— 5. 多订阅者:扇出与互不影响 —— const fan = signal(0); let a = 0; let b = 0; const disposeA = effect(() => { fan(); a += 1; }); effect(() => { fan(); b += 1; }); fan(1); ok('一次写值唤全部订阅者', a === 2 && b === 2); disposeA(); fan(2); ok('注销一个订阅者不影响另一个', a === 2 && b === 3); console.log(`\n${fail === 0 ? 'SMOKE OK' : 'SMOKE FAILED'}(${pass} 通过 / ${fail} 失败)`); process.exit(fail === 0 ? 0 : 1);