parent
3ceb94619a
commit
fe61f5be9b
@ -0,0 +1,134 @@
|
||||
// @vitest-environment jsdom
|
||||
|
||||
import { tick } from 'svelte';
|
||||
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 SomaRuntimeSources } from '$soma/runtime.svelte';
|
||||
import { DialogProvider } from '$soma/components/dialog/dialog-provider.svelte';
|
||||
|
||||
import {
|
||||
AlertDialogActionProvider,
|
||||
AlertDialogCancelProvider
|
||||
} from './alert-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 soma = {
|
||||
dom,
|
||||
langs: {
|
||||
ts: vi.fn((key: string) => (key.includes('cancel') ? 'Cancel' : 'Confirm'))
|
||||
},
|
||||
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);
|
||||
|
||||
return { dom };
|
||||
}
|
||||
|
||||
function actionOpts(el: HTMLButtonElement, ariaLabel: string | undefined = undefined) {
|
||||
return {
|
||||
id: state('alert-action'),
|
||||
ref: state<HTMLElement | null>(el),
|
||||
ariaLabel: state(ariaLabel)
|
||||
};
|
||||
}
|
||||
|
||||
function cancelOpts(el: HTMLButtonElement, ariaLabel: string | undefined = undefined) {
|
||||
return {
|
||||
id: state('alert-cancel'),
|
||||
ref: state<HTMLElement | null>(el),
|
||||
ariaLabel: state(ariaLabel)
|
||||
};
|
||||
}
|
||||
|
||||
describe('AlertDialogProvider parts', () => {
|
||||
afterEach(() => {
|
||||
vi.restoreAllMocks();
|
||||
document.body.innerHTML = '';
|
||||
});
|
||||
|
||||
it('projects action/cancel contracts and closes the backing Dialog', async () => {
|
||||
const { dom } = installSomaHarness();
|
||||
const handleClose = vi.fn();
|
||||
vi.spyOn(DialogProvider, 'require').mockReturnValue({
|
||||
handleClose
|
||||
} as unknown as DialogProvider);
|
||||
const actionEl = document.createElement('button');
|
||||
const cancelEl = document.createElement('button');
|
||||
document.body.append(actionEl, cancelEl);
|
||||
const actionOptions = actionOpts(actionEl);
|
||||
const cancelOptions = cancelOpts(cancelEl, 'Keep item');
|
||||
|
||||
const { result, cleanup } = withEffectRoot(() => {
|
||||
const action = AlertDialogActionProvider.create(actionOptions);
|
||||
const cancel = AlertDialogCancelProvider.create(cancelOptions);
|
||||
return { action, cancel };
|
||||
});
|
||||
|
||||
expect(result.action.resolvedAriaLabel).toBe('Confirm');
|
||||
expect(result.cancel.resolvedAriaLabel).toBe('Keep item');
|
||||
expect(result.action.props).toMatchObject({
|
||||
'data-alert-dialog-action': '',
|
||||
onclick: result.action.onclick
|
||||
});
|
||||
expect(result.cancel.props).toMatchObject({
|
||||
'data-alert-dialog-cancel': '',
|
||||
onclick: result.cancel.onclick
|
||||
});
|
||||
|
||||
await tick();
|
||||
expect(actionEl.getAttribute('type')).toBe('button');
|
||||
expect(actionEl.getAttribute('aria-label')).toBe('Confirm');
|
||||
expect(cancelEl.getAttribute('type')).toBe('button');
|
||||
expect(cancelEl.getAttribute('aria-label')).toBe('Keep item');
|
||||
|
||||
result.action.onclick({ currentTarget: actionEl } as never);
|
||||
result.cancel.onclick({ currentTarget: cancelEl } as never);
|
||||
expect(handleClose).toHaveBeenCalledTimes(2);
|
||||
|
||||
cleanup();
|
||||
dom.dispose();
|
||||
});
|
||||
|
||||
it('keeps aria-label reactive to explicit overrides', async () => {
|
||||
const { dom } = installSomaHarness();
|
||||
vi.spyOn(DialogProvider, 'require').mockReturnValue({
|
||||
handleClose: vi.fn()
|
||||
} as unknown as DialogProvider);
|
||||
const actionEl = document.createElement('button');
|
||||
document.body.appendChild(actionEl);
|
||||
const opts = actionOpts(actionEl);
|
||||
|
||||
const { result: action, cleanup } = withEffectRoot(() =>
|
||||
AlertDialogActionProvider.create(opts)
|
||||
);
|
||||
|
||||
await tick();
|
||||
expect(actionEl.getAttribute('aria-label')).toBe('Confirm');
|
||||
opts.ariaLabel.current = 'Delete account';
|
||||
await tick();
|
||||
expect(action.resolvedAriaLabel).toBe('Delete account');
|
||||
expect(actionEl.getAttribute('aria-label')).toBe('Delete account');
|
||||
|
||||
cleanup();
|
||||
dom.dispose();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in new issue