fix(admin): delete user button now works reliably #279

Merged
marcel merged 1 commits from fix/delete-user into main 2026-04-20 08:43:16 +02:00
2 changed files with 37 additions and 24 deletions

View File

@@ -15,16 +15,6 @@ const unsaved = createUnsavedWarning();
const selectedGroupIds = $derived(data.editUser.groups?.map((g: { id: string }) => g.id) ?? []);
let deleteFormEl = $state<HTMLFormElement | null>(null);
async function handleDelete() {
const confirmed = await confirm({
title: m.admin_user_delete_confirm({ username: data.editUser.email }),
destructive: true
});
if (confirmed) deleteFormEl!.requestSubmit();
}
$effect(() => {
if (form?.success) unsaved.clearOnSuccess();
});
@@ -51,10 +41,23 @@ $effect(() => {
<h2 class="flex-1 font-sans text-sm font-bold text-ink">
{m.admin_user_edit_heading({ username: data.editUser.email })}
</h2>
<form bind:this={deleteFormEl} method="POST" action="?/delete" use:enhance>
<form
method="POST"
action="?/delete"
use:enhance={async ({ cancel }) => {
const confirmed = await confirm({
title: m.admin_user_delete_confirm({ username: data.editUser.email }),
destructive: true
});
if (!confirmed) {
cancel();
} else {
unsaved.clearOnSuccess();
}
}}
>
<button
type="button"
onclick={handleDelete}
type="submit"
class="rounded-sm border border-red-200 bg-red-50 px-3 py-1 font-sans text-xs font-bold tracking-widest text-red-700 uppercase transition-colors hover:bg-red-100 dark:border-red-900 dark:bg-red-950/30 dark:text-red-400"
>
{m.btn_delete()}

View File

@@ -4,7 +4,17 @@ import { page } from 'vitest/browser';
import Page from './+page.svelte';
import { createConfirmService, CONFIRM_KEY } from '$lib/services/confirm.svelte.js';
vi.mock('$app/forms', () => ({ enhance: () => () => {} }));
const cancelMock = vi.hoisted(() => vi.fn());
vi.mock('$app/forms', () => ({
enhance: (form: HTMLFormElement, callback?: (args: { cancel: () => void }) => Promise<void>) => {
form.addEventListener('submit', async (e) => {
e.preventDefault();
if (callback) await callback({ cancel: cancelMock });
});
return () => {};
}
}));
vi.mock('$app/navigation', () => ({ beforeNavigate: vi.fn(), goto: vi.fn() }));
import { beforeNavigate, goto } from '$app/navigation';
@@ -161,39 +171,39 @@ describe('Admin edit user page feedback', () => {
// ─── Delete confirmation ──────────────────────────────────────────────────────
describe('Admin edit user page delete confirmation', () => {
it('delete button has type=button (does not submit natively)', async () => {
beforeEach(() => cancelMock.mockClear());
it('delete button has type=submit', async () => {
renderPage({ data: baseData, form: null });
const deleteForm = document.querySelector<HTMLFormElement>('form[action="?/delete"]')!;
const deleteBtn = deleteForm.querySelector('button') as HTMLButtonElement;
expect(deleteBtn.type).toBe('button');
expect(deleteBtn.type).toBe('submit');
});
it('does not submit delete form when user cancels', async () => {
it('calls cancel() and does not submit when user cancels', async () => {
const { service } = renderPage({ data: baseData, form: null });
const deleteForm = document.querySelector<HTMLFormElement>('form[action="?/delete"]')!;
const requestSubmit = vi.spyOn(deleteForm, 'requestSubmit').mockImplementation(() => {});
const deleteBtn = deleteForm.querySelector('button') as HTMLButtonElement;
const deleteBtn = deleteForm.querySelector('button[type="button"]') as HTMLButtonElement;
deleteBtn.click();
await vi.waitFor(() => expect(service.options).not.toBeNull());
service.settle(false);
await vi.waitFor(() => expect(service.options).toBeNull());
expect(requestSubmit).not.toHaveBeenCalled();
expect(cancelMock).toHaveBeenCalledOnce();
});
it('submits delete form when user confirms', async () => {
it('does not call cancel() and allows submit when user confirms', async () => {
const { service } = renderPage({ data: baseData, form: null });
const deleteForm = document.querySelector<HTMLFormElement>('form[action="?/delete"]')!;
const requestSubmit = vi.spyOn(deleteForm, 'requestSubmit').mockImplementation(() => {});
const deleteBtn = deleteForm.querySelector('button') as HTMLButtonElement;
const deleteBtn = deleteForm.querySelector('button[type="button"]') as HTMLButtonElement;
deleteBtn.click();
await vi.waitFor(() => expect(service.options).not.toBeNull());
service.settle(true);
await vi.waitFor(() => expect(service.options).toBeNull());
expect(requestSubmit).toHaveBeenCalledOnce();
expect(cancelMock).not.toHaveBeenCalled();
});
});