state/test/dom.test.js

89 lines
3.6 KiB
JavaScript
Raw Permalink Normal View History

// test/dom.test.js
import { RefreshState, $, $$ } from '@web/state';
import { NewState } from '@web/state';
export async function testDom() {
console.log('Testing dom.js...');
// 1. Basic $text binding
document.body.innerHTML = `<div id="test-node" $text="state.msg"></div>`;
window.state = NewState({ msg: 'hello' });
RefreshState(document.documentElement);
const node = $('#test-node');
if (node.textContent !== 'hello') throw new Error('$text binding failed');
state.msg = 'world';
if (node.textContent !== 'world') throw new Error('$text update failed');
// 2. $if directive
document.body.innerHTML = `
<div id="if-test">
<template $if="state.show">
<span id="if-content">Visible</span>
</template>
</div>
`;
state.show = false;
RefreshState(document.documentElement);
if ($('#if-content')) throw new Error('$if failed: should be hidden');
state.show = true;
await Promise.resolve();
if (!$('#if-content') || $('#if-content').textContent !== 'Visible') throw new Error('$if failed: should be visible');
state.show = false;
await Promise.resolve();
if ($('#if-content')) throw new Error('$if failed: should be hidden again');
// 3. $each directive (Index-based reuse)
document.body.innerHTML = `
<ul id="list-test">
<template $each="state.items" as="item" index="i">
<li $text="item.name"></li>
</template>
</ul>
`;
state.items = [{ name: 'A' }, { name: 'B' }];
RefreshState(document.documentElement);
await Promise.resolve(); // Wait for MutationObserver
let items = $$('#list-test li');
console.log('$each items length:', items.length);
if (items.length > 0) console.log('$each first item text:', items[0].textContent);
if (items.length !== 2 || items[0].textContent !== 'A' || items[1].textContent !== 'B') throw new Error('$each initialization failed');
const firstNode = items[0];
state.items = [{ name: 'A-mod' }, { name: 'B' }, { name: 'C' }];
await Promise.resolve();
items = $$('#list-test li');
if (items.length !== 3 || items[0].textContent !== 'A-mod') throw new Error('$each update failed');
if (items[0] !== firstNode) throw new Error('$each reuse failed: should reuse existing DOM nodes');
state.items = [{ name: 'C' }];
await Promise.resolve();
items = $$('#list-test li');
if (items.length !== 1 || items[0].textContent !== 'C') throw new Error('$each removal failed');
// 4. Two-way binding (bind)
document.body.innerHTML = `<input id="input-test" $bind="state.val">`;
state.val = 'initial';
RefreshState(document.documentElement);
await Promise.resolve();
const input = $('#input-test');
if (input.value !== 'initial') throw new Error('$bind initial value failed');
input.value = 'changed';
input.dispatchEvent(new Event('input'));
if (state.val !== 'changed') throw new Error('$bind write-back failed');
// 5. Unbinding cleanup (mock check)
// _unbindTree is called when nodes are removed via $if or $each.
// We verify that after $if=false, the state mappings for that node are cleared.
// This is hard to test directly without exposing internal Map, but we can check if it crashes.
state.show = true;
const ifNode = $('#if-content');
state.show = false; // Trigger _unbindTree via _clearRenderedNodes -> remove() -> MutationObserver (or manual in some cases)
// Actually MutationObserver handles _unbindTree in index.js.
console.log('dom.js tests passed');
return true;
}