Add high-risk Soma provider tests

active-uix
dev 5 months ago
parent 802ebce655
commit c6d24eeea3

@ -0,0 +1,68 @@
// @vitest-environment jsdom
import { describe, expect, it } from 'vitest';
import { CalendarDate, CalendarDateTime } from '$libs/days';
import { resolveInitialCalendarPlaceholder } from './calendar-provider.svelte';
import {
applyMultipleSelection,
applySingleSelection,
computeDateFlags,
createMonths
} from './helpers';
describe('CalendarProvider helpers', () => {
it('resolves the initial placeholder from selection before falling back to today', () => {
const selected = new CalendarDate(2026, 5, 15);
expect(resolveInitialCalendarPlaceholder(undefined, selected, 'single')).toBe(selected);
expect(resolveInitialCalendarPlaceholder(undefined, [selected], 'multiple')).toBe(selected);
});
it('generates fixed six-week month grids', () => {
const months = createMonths(new CalendarDate(2026, 2, 1), {
numberOfMonths: 2,
weekStartsOn: 1,
fixedWeeks: true,
locale: 'es-ES'
});
expect(months).toHaveLength(2);
expect(months[0].dates).toHaveLength(42);
expect(months[0].weeks).toHaveLength(6);
expect(months[1].dates).toHaveLength(42);
});
it('preserves time when single selection changes the calendar date', () => {
const previous = new CalendarDateTime(2026, 5, 15, 13, 45, 30);
const next = applySingleSelection(previous, new CalendarDate(2026, 6, 1), false);
expect(next).toBeInstanceOf(CalendarDateTime);
expect((next as CalendarDateTime).hour).toBe(13);
expect((next as CalendarDateTime).minute).toBe(45);
});
it('toggles multiple selection and resets when maxDays is exceeded', () => {
const first = new CalendarDate(2026, 5, 1);
const second = new CalendarDate(2026, 5, 2);
const third = new CalendarDate(2026, 5, 3);
expect(applyMultipleSelection([first], first, false, undefined)).toEqual([]);
expect(applyMultipleSelection([first, second], third, false, 2)).toEqual([third]);
});
it('composes disabled, unavailable and holiday flags independently', () => {
const day = new CalendarDate(2026, 5, 15);
const flags = computeDateFlags(day, {
minValue: new CalendarDate(2026, 5, 1),
maxValue: new CalendarDate(2026, 5, 31),
disabled: false,
isDateDisabled: undefined,
isDateUnavailable: (date) => date.day === 15,
isDateHoliday: (date) => date.day === 15
});
expect(flags).toEqual({ disabled: false, unavailable: true, holiday: true });
});
});

@ -0,0 +1,122 @@
// @vitest-environment jsdom
import { afterEach, describe, expect, it, vi } from 'vitest';
import { flushSync } from 'svelte';
import { createActiveDom } from '$adom';
import { state } from '$libs/reactive';
import type { Morfo } from '$uix/morfo';
import { Soma } from '$soma/core/soma.svelte';
import { createSomaRuntime, type SomaRuntimeSources } from '$soma/runtime.svelte';
import { CommandProvider } from './command-provider.svelte';
import { computeCommandScore } from './score';
function withEffectRoot<T>(fn: () => T): { result: T; cleanup: () => void } {
let result!: T;
const cleanup = $effect.root(() => {
result = fn();
});
return { result, cleanup };
}
function installSomaHarness() {
const dom = createActiveDom();
const soma = {
dom,
runtime: (morfo: Morfo, sources: Omit<SomaRuntimeSources, 'dom' | 'eventEngine'>) =>
createSomaRuntime(morfo, {
dom,
translate: (key) => key,
...sources
})
} as unknown as Soma;
vi.spyOn(Soma, 'require').mockReturnValue(soma);
vi.spyOn(CommandProvider.ctx, 'set').mockImplementation((value) => value);
return { dom };
}
function createCommandOpts() {
return {
id: state('command-root'),
ref: state<HTMLElement | null>(null),
value: state(''),
search: state(''),
label: state<string | undefined>('Commands'),
shouldFilter: state(true),
filter: state(computeCommandScore),
loop: state(false),
vimBindings: state(false),
columns: state(1),
disablePointerSelection: state(false),
dir: state<'ltr' | 'rtl'>('ltr')
};
}
function item(value: string, patch: Partial<Parameters<CommandProvider['registerItem']>[0]> = {}) {
return {
value,
keywords: [],
forceMount: false,
disabled: false,
groupValue: undefined,
element: null,
score: 1,
...patch
} as Parameters<CommandProvider['registerItem']>[0];
}
describe('CommandProvider', () => {
afterEach(() => {
vi.restoreAllMocks();
document.body.innerHTML = '';
});
it('filters visible items and auto-selects the first surviving value', () => {
const { dom } = installSomaHarness();
const opts = createCommandOpts();
opts.search.current = 'op';
const { result: provider, cleanup } = withEffectRoot(() => CommandProvider.create(opts));
provider.registerItem(item('Open'));
provider.registerItem(item('Save'));
provider.registerItem(item('Hidden', { forceMount: true }));
flushSync();
expect(provider.visibleOrderedValues).toEqual(['Open', 'Hidden']);
expect(provider.isVisible('Save')).toBe(false);
expect(opts.value.current).toBe('Open');
cleanup();
dom.dispose();
});
it('navigates through visible values and honours disabled selection handlers', () => {
const { dom } = installSomaHarness();
const opts = createCommandOpts();
opts.loop.current = true;
const onSelect = vi.fn();
const { result: provider, cleanup } = withEffectRoot(() => CommandProvider.create(opts));
provider.registerItem(item('Open'));
provider.registerItem(item('Close', { disabled: true }));
provider.registerOnSelect('Open', onSelect);
provider.registerOnSelect('Close', onSelect);
flushSync();
provider.last();
expect(opts.value.current).toBe('Close');
provider.selectCurrent();
expect(onSelect).not.toHaveBeenCalled();
provider.next();
expect(opts.value.current).toBe('Open');
provider.selectCurrent();
expect(onSelect).toHaveBeenCalledWith('Open');
cleanup();
dom.dispose();
});
});

@ -0,0 +1,81 @@
// @vitest-environment jsdom
import { describe, expect, it, vi } from 'vitest';
import { createActiveDom } from '$adom';
import { CalendarDate } from '$libs/days';
import {
areAllSegmentsFilledDOM,
getFirstSegment,
getValueFromSegmentsDOM,
handleSegmentNavigation,
initSegmentStates,
isAcceptableSegmentKey
} from '$soma/datetime';
function createField() {
const field = document.createElement('div');
for (const [id, part, text] of [
['month', 'month', '05'],
['literal', 'literal', '/'],
['day', 'day', '15'],
['year', 'year', '2026']
] as const) {
const segment = document.createElement('span');
segment.id = id;
segment.dataset.segment = part;
segment.textContent = text;
field.appendChild(segment);
}
document.body.appendChild(field);
return field;
}
describe('DateField segment helpers', () => {
it('creates fresh segment state maps for each field instance', () => {
const first = initSegmentStates();
const second = initSegmentStates();
first.day.updating = '1';
expect(second.day.updating).toBeNull();
expect(second.month.hasLeftFocus).toBe(true);
});
it('accepts numeric input and navigation keys only', () => {
expect(isAcceptableSegmentKey('7')).toBe(true);
expect(isAcceptableSegmentKey('ArrowRight')).toBe(true);
expect(isAcceptableSegmentKey('x')).toBe(false);
});
it('reads only rendered editable segments from the DOM', () => {
const field = createField();
expect(getFirstSegment(field)?.id).toBe('month');
expect(areAllSegmentsFilledDOM({ month: '05', day: '15', year: '2026' }, field)).toBe(true);
const value = getValueFromSegmentsDOM({
segmentObj: { month: '05', day: '15', year: '2026' },
fieldNode: field,
dateRef: new CalendarDate(2026, 1, 1)
});
expect(value.month).toBe(5);
expect(value.day).toBe(15);
expect(value.year).toBe(2026);
});
it('moves focus across segments through ActiveDom', () => {
const dom = createActiveDom();
const field = createField();
const month = field.querySelector<HTMLElement>('#month')!;
const focus = vi.spyOn(dom, 'focus');
const event = new KeyboardEvent('keydown', { key: 'ArrowRight' });
Object.defineProperty(event, 'currentTarget', { value: month });
handleSegmentNavigation(dom, event, field);
expect(focus).toHaveBeenCalledWith(field.querySelector('#day'));
dom.dispose();
});
});

@ -0,0 +1,121 @@
// @vitest-environment jsdom
import { afterEach, describe, expect, it, vi } from 'vitest';
import { createActiveDom } from '$adom';
import { state } from '$libs/reactive';
import type { Morfo } from '$uix/morfo';
import { Soma } from '$soma/core/soma.svelte';
import {
createSomaRuntime,
type EventEngineEmitter,
type SomaRuntimeSources
} from '$soma/runtime.svelte';
import { DialogProvider } from './dialog-provider.svelte';
function withEffectRoot<T>(fn: () => T): { result: T; cleanup: () => void } {
let result!: T;
const cleanup = $effect.root(() => {
result = fn();
});
return { result, cleanup };
}
function installSomaHarness() {
const dom = createActiveDom();
const eventEngine = { emit: vi.fn(async () => {}) } satisfies EventEngineEmitter;
const soma = {
dom,
events: eventEngine,
runtime: (morfo: Morfo, sources: Omit<SomaRuntimeSources, 'dom' | 'eventEngine'>) =>
createSomaRuntime(morfo, {
dom,
eventEngine,
translate: (key) => key,
...sources
})
} as unknown as Soma;
vi.spyOn(Soma, 'require').mockReturnValue(soma);
vi.spyOn(DialogProvider.ctx, 'getOr').mockReturnValue(undefined);
vi.spyOn(DialogProvider.ctx, 'set').mockImplementation((value) => value);
return { dom, eventEngine };
}
function createDialogOpts() {
return {
id: state('dialog-root'),
ref: state<HTMLElement | null>(null),
open: state(false),
disabled: state(false),
modal: state(true),
variant: state<'dialog' | 'alertdialog'>('dialog'),
intent: state<'neutral' | 'risk' | 'threat'>('neutral'),
onOpenChangeComplete: state(() => {})
};
}
describe('DialogProvider', () => {
afterEach(() => {
vi.restoreAllMocks();
document.body.innerHTML = '';
});
it('opens and closes through the morfo runtime without a registered content part', async () => {
const { dom, eventEngine } = installSomaHarness();
const opts = createDialogOpts();
const fallbackTarget = document.createElement('div');
document.body.appendChild(fallbackTarget);
const { result: provider, cleanup } = withEffectRoot(() => {
const p = DialogProvider.create(opts);
p.contentId.current = 'dialog-content';
p.runtime.part('content', {
id: state('dialog-content'),
ref: state<HTMLElement | null>(fallbackTarget),
syncAttrs: true
});
return p;
});
provider.handleOpen();
expect(opts.open.current).toBe(true);
await provider.triggerClose('close-dismiss', fallbackTarget);
expect(opts.open.current).toBe(false);
expect(fallbackTarget.getAttribute('data-last-action')).toBe('dismissed');
expect(fallbackTarget.getAttribute('data-state')).toBe('closed');
expect(eventEngine.emit).toHaveBeenCalledWith(
expect.objectContaining({
name: 'close-dismiss',
family: 'emerge',
target: fallbackTarget
})
);
cleanup();
dom.dispose();
});
it('does not mutate open state while disabled', () => {
const { dom } = installSomaHarness();
const opts = createDialogOpts();
opts.disabled.current = true;
opts.open.current = false;
const { result: provider, cleanup } = withEffectRoot(() => DialogProvider.create(opts));
provider.handleOpen();
expect(opts.open.current).toBe(false);
opts.open.current = true;
provider.handleClose();
expect(opts.open.current).toBe(true);
cleanup();
dom.dispose();
});
});

@ -0,0 +1,151 @@
// @vitest-environment jsdom
import { afterEach, describe, expect, it, vi } from 'vitest';
import { createActiveDom } from '$adom';
import { state } from '$libs/reactive';
import type { Morfo } from '$uix/morfo';
import { Soma } from '$soma/core/soma.svelte';
import {
createSomaRuntime,
type EventEngineEmitter,
type SomaRuntimeSources
} from '$soma/runtime.svelte';
import { DrawerProvider } from './drawer-provider.svelte';
import type { DrawerDirection, DrawerSnapPoint, DrawerVariant } from './types';
function withEffectRoot<T>(fn: () => T): { result: T; cleanup: () => void } {
let result!: T;
const cleanup = $effect.root(() => {
result = fn();
});
return { result, cleanup };
}
function installSomaHarness(dir: 'ltr' | 'rtl' = 'ltr') {
const dom = createActiveDom();
const eventEngine = { emit: vi.fn(async () => {}) } satisfies EventEngineEmitter;
const soma = {
dom,
events: eventEngine,
prefs: { getDir: () => dir },
runtime: (morfo: Morfo, sources: Omit<SomaRuntimeSources, 'dom' | 'eventEngine'>) =>
createSomaRuntime(morfo, {
dom,
eventEngine,
translate: (key) => key,
...sources
})
} as unknown as Soma;
vi.spyOn(Soma, 'require').mockReturnValue(soma);
vi.spyOn(DrawerProvider.ctx, 'getOr').mockReturnValue(undefined);
vi.spyOn(DrawerProvider.ctx, 'set').mockImplementation((value) => value);
return { dom, eventEngine };
}
function createDrawerOpts() {
return {
id: state('drawer-root'),
ref: state<HTMLElement | null>(null),
open: state(false),
activeSnapPoint: state<DrawerSnapPoint | null>(null),
isDragging: state(false),
disabled: state(false),
variant: state<DrawerVariant>('overlay'),
dismissible: state(true),
handleOnly: state(false),
dragToDismiss: state(false),
direction: state<DrawerDirection>('bottom'),
snapPoints: state<DrawerSnapPoint[] | undefined>(undefined),
snapToSequential: state(false),
closeThreshold: state(0.25),
velocityThreshold: state(0.5),
scrollLockTimeout: state(500),
minSize: state<number | string | undefined>(undefined),
maxSize: state<number | string | undefined>(undefined),
onOpenChangeComplete: state(() => {}),
onDragStart: state<((event: PointerEvent) => void) | undefined>(undefined),
onDrag: state<((event: PointerEvent, progress: number) => void) | undefined>(undefined),
onRelease: state<((event: PointerEvent, willClose: boolean) => void) | undefined>(undefined),
onActiveSnapPointChange: state(() => {})
};
}
describe('DrawerProvider', () => {
afterEach(() => {
vi.restoreAllMocks();
document.body.innerHTML = '';
});
it('resolves logical directions from the active UI direction preference', () => {
const { dom } = installSomaHarness('rtl');
const opts = createDrawerOpts();
opts.direction.current = 'start';
const { result: provider, cleanup } = withEffectRoot(() => DrawerProvider.create(opts));
expect(provider.resolvedSide).toBe('right');
cleanup();
dom.dispose();
});
it('dismisses through the morfo runtime and clears transient snap state', async () => {
const { dom, eventEngine } = installSomaHarness();
const opts = createDrawerOpts();
opts.open.current = true;
opts.activeSnapPoint.current = 0.5;
const fallbackTarget = document.createElement('div');
document.body.appendChild(fallbackTarget);
const { result: provider, cleanup } = withEffectRoot(() => {
const p = DrawerProvider.create(opts);
p.contentId.current = 'drawer-content';
p.runtime.part('content', {
id: state('drawer-content'),
ref: state<HTMLElement | null>(fallbackTarget),
syncAttrs: true
});
return p;
});
await provider.triggerClose('close-dismiss', fallbackTarget);
expect(opts.open.current).toBe(false);
expect(opts.activeSnapPoint.current).toBeNull();
expect(fallbackTarget.getAttribute('data-last-action')).toBe('dismissed');
expect(fallbackTarget.getAttribute('data-state')).toBe('closed');
expect(eventEngine.emit).toHaveBeenCalledWith(
expect.objectContaining({
name: 'close-dismiss',
family: 'emerge',
target: fallbackTarget
})
);
cleanup();
dom.dispose();
});
it('keeps persistent drawers structurally open', () => {
const { dom } = installSomaHarness();
const opts = createDrawerOpts();
opts.variant.current = 'persistent';
opts.open.current = false;
const { result: provider, cleanup } = withEffectRoot(() => DrawerProvider.create(opts));
provider.handleOpen();
expect(opts.open.current).toBe(false);
opts.open.current = true;
provider.handleClose();
expect(opts.open.current).toBe(true);
cleanup();
dom.dispose();
});
});
Loading…
Cancel
Save

Powered by TurnKey Linux.