+ {#if query.isPending}
+
Loading what the last search found…
+ {:else if query.isError}
+
Couldn't load the releases.
+ {:else if items.length === 0}
+
+ Nothing kept from a search yet. Search now, or wait for the schedule.
+
+ {:else}
+
+ {items.length === 1 ? '1 release' : `${items.length} releases`} found {relativeTime(items[0].searched_at)}, best first.
+
+
+ {/if}
+
diff --git a/web/src/lib/components/WantCandidates.test.ts b/web/src/lib/components/WantCandidates.test.ts
new file mode 100644
index 00000000..3c7fcecf
--- /dev/null
+++ b/web/src/lib/components/WantCandidates.test.ts
@@ -0,0 +1,90 @@
+import { afterEach, describe, expect, test, vi } from 'vitest';
+import { fireEvent, render, screen, waitFor } from '@testing-library/svelte';
+import { mockQuery } from '../../test-utils/query';
+import type { Candidate } from '#lib/api/types.js';
+
+vi.mock('#lib/api/wanted.js', () => ({
+ createCandidatesQuery: vi.fn(),
+ grabCandidate: vi.fn().mockResolvedValue({ added: true, refused: false, gone: false, unconfigured: false, kept: false, replaced: false }),
+ blockCandidate: vi.fn().mockResolvedValue(undefined),
+ unblockRelease: vi.fn().mockResolvedValue(undefined)
+}));
+vi.mock('#lib/stores/toast.svelte.js', () => ({ pushToast: vi.fn() }));
+
+import WantCandidates from './WantCandidates.svelte';
+import { blockCandidate, createCandidatesQuery, grabCandidate, unblockRelease } from '#lib/api/wanted.js';
+import { pushToast } from '#lib/stores/toast.svelte.js';
+
+function cand(key: string, extra: Partial