refactor(transcription): replace window.confirm with ConfirmService in TranscriptionBlock

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Marcel
2026-04-12 13:47:37 +02:00
parent 08bd27b5cd
commit d4ead08c17
3 changed files with 109 additions and 33 deletions

View File

@@ -1,7 +1,10 @@
<script lang="ts">
import { m } from '$lib/paraglide/messages.js';
import { getConfirmService } from '$lib/services/confirm.svelte.js';
import CommentThread from './CommentThread.svelte';
const { confirm } = getConfirmService();
type SaveState = 'idle' | 'saving' | 'saved' | 'fading' | 'error';
type Props = {
@@ -90,10 +93,12 @@ function handleInput(event: Event) {
onTextChange(target.value);
}
function handleDelete() {
if (confirm(m.transcription_block_delete_confirm())) {
onDeleteClick();
}
async function handleDelete() {
const confirmed = await confirm({
title: m.transcription_block_delete_confirm(),
destructive: true
});
if (confirmed) onDeleteClick();
}
function handleTextareaMouseUp() {

View File

@@ -1,25 +1,32 @@
import { describe, it, expect, vi, afterEach } from 'vitest';
import { cleanup, render } from 'vitest-browser-svelte';
import { page } from 'vitest/browser';
import TranscriptionBlock from './TranscriptionBlock.svelte';
import TranscriptionBlockHost from './TranscriptionBlock.test-host.svelte';
import type { ConfirmService } from '$lib/services/confirm.svelte.js';
afterEach(cleanup);
const BASE_PROPS = {
blockId: 'block-1',
documentId: 'doc-1',
blockNumber: 3,
text: 'Liebe Mutter,',
label: null,
active: false,
saveState: 'idle' as const,
canComment: true,
currentUserId: 'user-1',
onTextChange: vi.fn(),
onFocus: vi.fn(),
onDeleteClick: vi.fn(),
onRetry: vi.fn()
};
// Renders TranscriptionBlock via the host, which provides ConfirmService context.
function renderBlock(overrides: Record<string, unknown> = {}) {
return render(TranscriptionBlock, {
blockId: 'block-1',
documentId: 'doc-1',
blockNumber: 3,
text: 'Liebe Mutter,',
label: null,
active: false,
saveState: 'idle' as const,
canComment: true,
currentUserId: 'user-1',
onTextChange: vi.fn(),
onFocus: vi.fn(),
onDeleteClick: vi.fn(),
onRetry: vi.fn(),
return render(TranscriptionBlockHost, {
...BASE_PROPS,
onServiceReady: () => {},
...overrides
});
}
@@ -162,29 +169,57 @@ describe('TranscriptionBlock — reorder controls', () => {
// ─── Delete confirmation ──────────────────────────────────────────────────────
describe('TranscriptionBlock — delete confirmation', () => {
it('does not call onDeleteClick when user cancels confirm dialog', async () => {
const onDeleteClick = vi.fn();
vi.spyOn(window, 'confirm').mockReturnValue(false);
renderBlock({ onDeleteClick });
function renderBlockWithService(overrides: Record<string, unknown> = {}) {
let service!: ConfirmService;
render(TranscriptionBlockHost, {
blockId: 'block-1',
documentId: 'doc-1',
blockNumber: 3,
text: 'Liebe Mutter,',
label: null,
active: false,
saveState: 'idle' as const,
canComment: true,
currentUserId: 'user-1',
onTextChange: vi.fn(),
onFocus: vi.fn(),
onDeleteClick: vi.fn(),
onRetry: vi.fn(),
onServiceReady: (s: ConfirmService) => {
service = s;
},
...overrides
});
return { service };
}
const deleteBtn = page.getByRole('button', { name: 'Löschen' });
await deleteBtn.click();
describe('TranscriptionBlock — delete confirmation', () => {
it('does not call onDeleteClick when user cancels via confirm service', async () => {
const onDeleteClick = vi.fn();
const { service } = renderBlockWithService({ onDeleteClick });
// Use native DOM click so the async handler starts but yields at the await,
// letting the test observe service.options and settle the promise.
const deleteBtn = document.querySelector('button[aria-label="Löschen"]') as HTMLButtonElement;
deleteBtn.click();
await vi.waitFor(() => expect(service.options).not.toBeNull());
service.settle(false);
await vi.waitFor(() => expect(service.options).toBeNull());
expect(onDeleteClick).not.toHaveBeenCalled();
vi.restoreAllMocks();
});
it('calls onDeleteClick when user confirms deletion', async () => {
it('calls onDeleteClick when user confirms via confirm service', async () => {
const onDeleteClick = vi.fn();
vi.spyOn(window, 'confirm').mockReturnValue(true);
renderBlock({ onDeleteClick });
const { service } = renderBlockWithService({ onDeleteClick });
const deleteBtn = page.getByRole('button', { name: 'Löschen' });
await deleteBtn.click();
const deleteBtn = document.querySelector('button[aria-label="Löschen"]') as HTMLButtonElement;
deleteBtn.click();
await vi.waitFor(() => expect(service.options).not.toBeNull());
service.settle(true);
await vi.waitFor(() => expect(service.options).toBeNull());
expect(onDeleteClick).toHaveBeenCalledOnce();
vi.restoreAllMocks();
});
});

View File

@@ -0,0 +1,36 @@
<script lang="ts">
import { provideConfirmService, type ConfirmService } from '$lib/services/confirm.svelte.js';
import TranscriptionBlock from './TranscriptionBlock.svelte';
type BlockProps = {
blockId: string;
documentId: string;
blockNumber: number;
text: string;
label: string | null;
active: boolean;
saveState: 'idle' | 'saving' | 'saved' | 'fading' | 'error';
canComment: boolean;
currentUserId: string | null;
onTextChange: (text: string) => void;
onFocus: () => void;
onDeleteClick: () => void;
onRetry: () => void;
onMoveUp?: () => void;
onMoveDown?: () => void;
isFirst?: boolean;
isLast?: boolean;
};
let {
onServiceReady,
...blockProps
}: BlockProps & {
onServiceReady: (s: ConfirmService) => void;
} = $props();
const service = provideConfirmService();
onServiceReady(service);
</script>
<TranscriptionBlock {...blockProps} />