import assert from "node:assert/strict"; import { readFileSync } from "node:fs"; import { join } from "node:path"; import { fileURLToPath } from "node:url"; import test from "node:test"; const root = fileURLToPath(new URL("..", import.meta.url)); function readProjectFile(path: string): string { return readFileSync(join(root, path), "utf8"); } function functionBody(source: string, functionName: string): string { const start = source.indexOf(`const ${functionName} = useCallback`); assert.notEqual(start, -1, `${functionName} should exist`); const nextConst = source.indexOf("\n const ", start + 1); const nextHook = source.indexOf("\n use", start + 1); const candidates = [nextConst, nextHook].filter((index) => index > start); const end = candidates.length > 0 ? Math.min(...candidates) : source.length; return source.slice(start, end); } test("host and AI provider deletion use in-app confirmation dialogs", () => { const appSource = readProjectFile("application/app/AppSideEffects.tsx"); // App owns the confirm state; AppShell renders the dialog. const appShellSource = readProjectFile("application/app/AppShell.tsx"); const aiSettingsSource = readProjectFile("components/settings/tabs/SettingsAITab.tsx"); assert.match(appShellSource, /import \{ ConfirmDialog \} from '\.\.\/\.\.\/components\/ui\/confirm-dialog';/); assert.match(appShellSource, / { const confirmDialogSource = readProjectFile("components/ui/confirm-dialog.tsx"); const vaultDeleteDialogSource = readProjectFile("components/vault/VaultDeleteConfirmDialog.tsx"); const sftpDialogSource = readProjectFile("components/sftp/SftpPaneDialogs.tsx"); const sftpClipboardUploadDialogSource = readProjectFile("components/sftp/SftpClipboardUploadDialog.tsx"); const hostTreeGroupDeleteSource = readProjectFile("components/host/HostTreeGroupDeleteDialog.tsx"); const vaultViewLayoutSource = readProjectFile("components/vault/VaultViewLayout.tsx"); assert.match(confirmDialogSource, /DialogTitle className="truncate"/); assert.match(confirmDialogSource, /overflow-hidden sm:max-w-\[380px\]/); assert.match(vaultDeleteDialogSource, /\{title\}<\/span>/); assert.match(vaultDeleteDialogSource, /overflow-hidden sm:max-w-\[400px\]/); assert.match(sftpDialogSource, //); assert.match(sftpDialogSource, /\{name\}<\/span>/); assert.match(sftpDialogSource, /overflow-hidden sm:max-w-sm/); assert.match(sftpClipboardUploadDialogSource, /overflow-hidden sm:max-w-md/); assert.match(sftpClipboardUploadDialogSource, //); assert.match( sftpClipboardUploadDialogSource, /min-w-0 rounded-md border border-border\/60 bg-muted\/30 px-3 py-2 text-sm font-mono break-all \[overflow-wrap:anywhere\]/, ); assert.match(hostTreeGroupDeleteSource, /overflow-hidden sm:max-w-lg/); assert.match(hostTreeGroupDeleteSource, /break-words text-sm text-muted-foreground \[overflow-wrap:anywhere\]/); assert.match(vaultViewLayoutSource, /overflow-hidden sm:max-w-lg/); assert.match(vaultViewLayoutSource, /break-words text-sm text-muted-foreground \[overflow-wrap:anywhere\]/); });