1import { describe, expect, it } from "vitest";2import type { PasskeyData, VaultEntry } from "../../shared/types";3import { filterAndSortVaultEntries } from "./vault-search";45function basePasskey(overrides: Partial<PasskeyData> = {}): PasskeyData {6 return {7 rpId: "example.com",8 rpName: "Example",9 credentialId: "cred-1",10 publicKeyCose: "cose",11 userHandle: "handle",12 userName: "passkey-user",13 userDisplayName: "Passkey User",14 algorithm: -7,15 signCount: 0,16 aaguid: "",17 backupEligible: false,18 backupState: false,19 createdAt: 1,20 ...overrides,21 };22}2324function entry(overrides: Partial<VaultEntry> = {}): VaultEntry {25 return {26 id: "entry-1",27 title: "Example",28 type: "login",29 createdAt: 1,30 modifiedAt: 1,31 ...overrides,32 };33}3435describe("vault-search", () => {36 it("sorts recent entries by last-used timestamps", () => {37 const entries = [38 entry({ id: "created-only", title: "Created only", modifiedAt: 10 }),39 entry({ id: "login-used", title: "Login used", lastUsedAt: 300, modifiedAt: 20 }),40 entry({ id: "passkey-used", title: "Passkey used", passkey: basePasskey({ lastUsedAt: 200 }) }),41 ];4243 const sorted = filterAndSortVaultEntries(entries, {44 notesOnly: false,45 selectedTags: new Set(),46 query: "",47 searchScope: "both",48 sortOrder: "recent",49 });5051 expect(sorted.map((candidate) => candidate.id)).toEqual([52 "login-used",53 "passkey-used",54 "created-only",55 ]);56 });5758 it("respects the email and username search scope toggle", () => {59 const sample = entry({60 id: "identity",61 title: "Mailbox",62 username: "june-user",63 email: "[email protected]",64 url: "https://mail.example.com",65 });6667 const emailsOnly = filterAndSortVaultEntries([sample], {68 notesOnly: false,69 selectedTags: new Set(),70 query: "june-user",71 searchScope: "emails",72 sortOrder: "recent",73 });74 const usernamesOnly = filterAndSortVaultEntries([sample], {75 notesOnly: false,76 selectedTags: new Set(),77 query: "june-user",78 searchScope: "usernames",79 sortOrder: "recent",80 });81 const both = filterAndSortVaultEntries([sample], {82 notesOnly: false,83 selectedTags: new Set(),84 query: "june-user",85 searchScope: "both",86 sortOrder: "recent",87 });8889 expect(emailsOnly).toHaveLength(0);90 expect(usernamesOnly).toHaveLength(1);91 expect(both).toHaveLength(1);92 });9394 it("uses recent usage as the tie-breaker for matching search results", () => {95 const entries = [96 entry({97 id: "older",98 title: "Alpha",99 username: "alpha-user",100 modifiedAt: 10,101 lastUsedAt: 100,102 }),103 entry({104 id: "newer",105 title: "Alpha",106 username: "alpha-user",107 modifiedAt: 20,108 lastUsedAt: 200,109 }),110 entry({111 id: "fallback",112 title: "Alpha",113 username: "alpha-user",114 modifiedAt: 30,115 }),116 ];117118 const sorted = filterAndSortVaultEntries(entries, {119 notesOnly: false,120 selectedTags: new Set(),121 query: "alpha",122 searchScope: "usernames",123 sortOrder: "recent",124 });125126 expect(sorted.map((candidate) => candidate.id)).toEqual([127 "newer",128 "older",129 "fallback",130 ]);131 });132});