lupine.web
Version:
lupine.web is a extremely fast, small size and lightweight frontend framework, using React TSX syntax.
96 lines (77 loc) • 3.65 kB
text/typescript
import './export-lupine';
import { describe, it, mock, beforeEach, afterEach } from 'node:test';
import assert from 'node:assert/strict';
import {
ComponentStateStore,
evaluateComponentWithStore,
getCurrentStore,
useState
} from './use-state';
import { domUniqueId } from './render-attribute';
describe('use-state Core Engine', () => {
beforeEach(() => {
mock.method(Date, 'now', () => 1775190809080);
domUniqueId(true); // wipe cleanly
});
afterEach(() => {
mock.restoreAll();
});
describe('evaluateComponentWithStore', () => {
it('should successfully evaluate a synchronous component and return VNode', async () => {
const SyncComp = (props: { text: string }) => {
return { type: 'div', props: { children: [props.text] } };
};
const store = new ComponentStateStore(SyncComp, { text: 'Sync World' });
const result = await evaluateComponentWithStore(store);
assert.equal(result.type, 'div');
assert.deepEqual(result.props.children, ['Sync World']);
});
it('should successfully evaluate an asynchronous component and return VNode', async () => {
const AsyncComp = async (props: { text: string }) => {
await new Promise(r => setTimeout(r, 1)); // simulate async
return { type: 'span', props: { children: [props.text] } };
};
const store = new ComponentStateStore(AsyncComp, { text: 'Async World' });
const result = await evaluateComponentWithStore(store);
assert.equal(result.type, 'span');
assert.deepEqual(result.props.children, ['Async World']);
});
it('should set and clear the global currentStore during execution', async () => {
let storeInsideComp: any = undefined;
const TestComp = () => {
storeInsideComp = getCurrentStore();
return { type: 'div', props: {} };
};
assert.equal(getCurrentStore(), null); // Before
const store = new ComponentStateStore(TestComp, {});
await evaluateComponentWithStore(store);
assert.equal(storeInsideComp, store); // Inside component: pointer should be active
assert.equal(getCurrentStore(), null); // After evaluation: pointer must be cleared
});
it('should inject ref wrapper onto VNode if a hook is used', async () => {
const HookComp = () => {
useState(0); // This sets store.hookIndex > 0
return { type: 'section', props: { 'data-id': 'hooked' } };
};
const store = new ComponentStateStore(HookComp, {});
const result = await evaluateComponentWithStore(store);
// The returned VNode should now have a ref object generated by buildStateRef
assert.ok(result.props.ref, 'Ref should be injected because a hook was used');
assert.equal(typeof result.props.ref.onLoad, 'function');
assert.equal(typeof result.props.ref.refresh, 'function');
});
it('should inject ref wrapper onto VNode if user already passed a ref', async () => {
const userRef = { current: null, referToCssId: 'test-global-css' };
const RefComp = () => {
// No hook used here, but user passes an existing ref
return { type: 'div', props: { ref: userRef } };
};
const store = new ComponentStateStore(RefComp, {});
const result = await evaluateComponentWithStore(store);
assert.ok(result.props.ref);
assert.equal(result.props.ref, userRef, 'It should merge into the original user ref object');
assert.equal(result.props.ref.referToCssId, 'test-global-css');
assert.equal(typeof result.props.ref.onLoad, 'function', 'Wrapper added successfully');
});
});
});