fix(domtools): stabilize DomTools lifecycle, cleanup, and singleton behavior
This commit is contained in:
+114
-3
@@ -1,9 +1,120 @@
|
||||
import { expect, tap } from '@git.zone/tstest/tapbundle';
|
||||
import * as domtools from '../ts/index.js';
|
||||
|
||||
tap.test('first test', async () => {
|
||||
const domtoolsInstance = await domtools.DomTools.setupDomTools();
|
||||
expect(domtoolsInstance).toBeInstanceOf(domtools.DomTools);
|
||||
const cleanupGlobalDomTools = () => {
|
||||
try {
|
||||
domtools.DomTools.getGlobalDomToolsSync().dispose();
|
||||
} catch {
|
||||
// No global domtools instance exists yet.
|
||||
}
|
||||
};
|
||||
|
||||
const getStyleCount = () => document.head.querySelectorAll('style').length;
|
||||
|
||||
tap.test('setupDomTools reuses the global singleton', async () => {
|
||||
cleanupGlobalDomTools();
|
||||
const firstInstance = await domtools.DomTools.setupDomTools();
|
||||
const secondInstance = await domtools.DomTools.setupDomTools();
|
||||
|
||||
expect(firstInstance).toBeInstanceOf(domtools.DomTools);
|
||||
expect(firstInstance === secondInstance).toBeTrue();
|
||||
expect(domtools.DomTools.getGlobalDomToolsSync() === firstInstance).toBeTrue();
|
||||
|
||||
cleanupGlobalDomTools();
|
||||
});
|
||||
|
||||
tap.test('ignoreGlobal creates an isolated instance without replacing the global one', async () => {
|
||||
cleanupGlobalDomTools();
|
||||
const globalInstance = await domtools.DomTools.setupDomTools();
|
||||
const isolatedInstance = await domtools.DomTools.setupDomTools({
|
||||
ignoreGlobal: true,
|
||||
});
|
||||
|
||||
await globalInstance.domToolsReady.promise;
|
||||
await globalInstance.domReady.promise;
|
||||
await isolatedInstance.domToolsReady.promise;
|
||||
await isolatedInstance.domReady.promise;
|
||||
|
||||
expect(globalInstance === isolatedInstance).toBeFalse();
|
||||
expect(domtools.DomTools.getGlobalDomToolsSync() === globalInstance).toBeTrue();
|
||||
expect(Boolean(isolatedInstance.keyboard)).toBeTrue();
|
||||
|
||||
isolatedInstance.dispose();
|
||||
cleanupGlobalDomTools();
|
||||
});
|
||||
|
||||
tap.test('runOnce shares the first result across concurrent callers', async () => {
|
||||
cleanupGlobalDomTools();
|
||||
const domtoolsInstance = await domtools.DomTools.setupDomTools({
|
||||
ignoreGlobal: true,
|
||||
});
|
||||
let runCount = 0;
|
||||
|
||||
const [firstResult, secondResult] = await Promise.all([
|
||||
domtoolsInstance.runOnce('test-run-once', async () => {
|
||||
runCount += 1;
|
||||
await new Promise<void>((resolve) => window.setTimeout(resolve, 10));
|
||||
return 'shared-result';
|
||||
}),
|
||||
domtoolsInstance.runOnce('test-run-once', async () => {
|
||||
runCount += 1;
|
||||
return 'unexpected-result';
|
||||
}),
|
||||
]);
|
||||
|
||||
expect(runCount).toEqual(1);
|
||||
expect(firstResult).toEqual('shared-result');
|
||||
expect(secondResult).toEqual('shared-result');
|
||||
|
||||
domtoolsInstance.dispose();
|
||||
});
|
||||
|
||||
tap.test('elementBasic setup waits for global styles and keeps injection idempotent', async () => {
|
||||
cleanupGlobalDomTools();
|
||||
const styleCountBefore = getStyleCount();
|
||||
|
||||
const firstInstance = await domtools.elementBasic.setup();
|
||||
const secondInstance = await domtools.elementBasic.setup();
|
||||
|
||||
await firstInstance.globalStylesReady.promise;
|
||||
|
||||
expect(firstInstance === secondInstance).toBeTrue();
|
||||
expect(getStyleCount() - styleCountBefore).toEqual(1);
|
||||
|
||||
firstInstance.dispose();
|
||||
expect(getStyleCount()).toEqual(styleCountBefore);
|
||||
expect(() => domtools.DomTools.getGlobalDomToolsSync()).toThrow();
|
||||
});
|
||||
|
||||
tap.test('keyboard combos still emit through the public key enum API', async () => {
|
||||
const domtoolsInstance = await domtools.DomTools.setupDomTools({
|
||||
ignoreGlobal: true,
|
||||
});
|
||||
await domtoolsInstance.domReady.promise;
|
||||
|
||||
const keyboard = domtoolsInstance.keyboard!;
|
||||
const events: KeyboardEvent[] = [];
|
||||
const subscription = keyboard.on([keyboard.keyEnum.Ctrl, keyboard.keyEnum.S]).subscribe((event) => {
|
||||
events.push(event);
|
||||
});
|
||||
|
||||
keyboard.triggerKeyPress([keyboard.keyEnum.Ctrl, keyboard.keyEnum.S]);
|
||||
|
||||
expect(events).toHaveLength(1);
|
||||
expect(events[0].key).toEqual('s');
|
||||
expect(events[0].ctrlKey).toBeTrue();
|
||||
|
||||
subscription.unsubscribe();
|
||||
domtoolsInstance.dispose();
|
||||
});
|
||||
|
||||
tap.test('plugin namespace exports remain available for downstream consumers', async () => {
|
||||
expect(domtools.plugins.smartdelay.delayFor).toBeDefined();
|
||||
expect(domtools.plugins.smartstate.Smartstate).toBeDefined();
|
||||
expect(domtools.plugins.smartpromise.defer).toBeDefined();
|
||||
expect(domtools.plugins.smartrouter.SmartRouter).toBeDefined();
|
||||
expect(domtools.plugins.smartrx.rxjs.Subject).toBeDefined();
|
||||
expect(domtools.plugins.smarturl.Smarturl).toBeDefined();
|
||||
});
|
||||
|
||||
export default tap.start();
|
||||
|
||||
Reference in New Issue
Block a user