package com.peachpasswords.ui.vault import com.peachpasswords.vault.VaultEntry import org.junit.Assert.assertEquals import org.junit.Assert.assertFalse import org.junit.Assert.assertTrue import org.junit.Test class EntryEditScreenTest { // ── Top-bar label (cancel | | save) ─────────────────────────────── @Test fun title_bar_label_is_type_specific_for_new_entries() { assertEquals("new entry", editTitleBarLabel(isNew = true, type = "login")) assertEquals("new note", editTitleBarLabel(isNew = true, type = "note")) assertEquals("new card", editTitleBarLabel(isNew = true, type = "card")) } @Test fun title_bar_label_is_edit_for_existing_entries() { assertEquals("edit", editTitleBarLabel(isNew = false, type = "login")) assertEquals("edit", editTitleBarLabel(isNew = false, type = "note")) } @Test fun primary_action_label_matches_browser_add_view() { assertEquals("create", entryEditPrimaryActionLabel(isNew = true, saving = false)) assertEquals("save", entryEditPrimaryActionLabel(isNew = false, saving = false)) assertEquals("saving...", entryEditPrimaryActionLabel(isNew = true, saving = true)) } @Test fun save_is_disabled_until_browser_minimum_fields_are_present() { assertFalse(canSaveEditForm(EditForm(type = "login", title = ""))) assertTrue(canSaveEditForm(EditForm(type = "login", title = "GitHub"))) assertFalse(canSaveEditForm(EditForm(type = "card", title = "Visa", cardholderName = "", cardNumber = "4242"))) assertFalse(canSaveEditForm(EditForm(type = "card", title = "Visa", cardholderName = "June", cardNumber = ""))) assertTrue(canSaveEditForm(EditForm(type = "card", title = "Visa", cardholderName = "June", cardNumber = "4242"))) } // ── New note routes to NotesMode, not the form ──────────────────────────── @Test fun new_note_routes_to_notes_mode() { assertTrue(shouldOpenNotesMode(isNew = true, type = "note")) // Existing note stays in the form; new login/card stay in the form. assertFalse(shouldOpenNotesMode(isNew = false, type = "note")) assertFalse(shouldOpenNotesMode(isNew = true, type = "login")) assertFalse(shouldOpenNotesMode(isNew = true, type = "card")) } // ── Ghost watermark fades in as the title is typed ──────────────────────── @Test fun new_entry_ghost_watermark_appears_on_title_type() { assertEquals(0f, editGhostWatermarkTarget(""), 0f) assertEquals(0f, editGhostWatermarkTarget(" "), 0f) assertEquals(0.03f, editGhostWatermarkTarget("GitHub"), 0f) } // ── Browser AddView optional login fields ──────────────────────────────── @Test fun new_login_hides_optional_totp_and_notes_until_requested() { assertFalse(shouldShowLoginTotp("login", revealed = false, totp = "")) assertFalse(shouldShowLoginNotes("login", revealed = false, notes = "")) assertTrue(shouldShowAddTotpLink("login", revealed = false, totp = "")) assertTrue(shouldShowAddNotesLink("login", revealed = false, notes = "")) } @Test fun optional_login_fields_show_when_revealed_or_populated() { assertTrue(shouldShowLoginTotp("login", revealed = true, totp = "")) assertTrue(shouldShowLoginTotp("login", revealed = false, totp = "JBSWY3DPEHPK3PXP")) assertTrue(shouldShowLoginNotes("login", revealed = true, notes = "")) assertTrue(shouldShowLoginNotes("login", revealed = false, notes = "remember this")) assertFalse(shouldShowAddTotpLink("login", revealed = true, totp = "")) assertFalse(shouldShowAddNotesLink("login", revealed = false, notes = "remember this")) } @Test fun identity_suggestions_split_username_and_email_like_browser() { val entries = listOf( identityEntry("a", username = "june", email = "june@example.com"), identityEntry("b", username = "june", email = null), identityEntry("c", username = "billing@example.com", email = "ops@example.com"), identityEntry("d", username = "billing@example.com", email = null), ) assertEquals(listOf("june"), identitySuggestionsFor(entries, IdentityField.Username)) assertEquals( listOf("billing@example.com", "june@example.com", "ops@example.com"), identitySuggestionsFor(entries, IdentityField.Email), ) } @Test fun generator_defaults_and_toggle_label_match_browser_add_view() { assertEquals(64, BROWSER_ADD_GENERATOR_LENGTH) assertEquals("generate", generatorToggleLabel(open = false)) assertEquals("hide generator", generatorToggleLabel(open = true)) } @Test fun tag_suggestions_match_browser_add_view_limit_and_filtering() { val all = listOf("banking", "billing", "books", "business", "backup", "travel") assertEquals( listOf("banking", "billing", "books", "business", "backup"), tagSuggestionsFor(input = "b", allTags = all, tags = emptyList()), ) assertEquals( listOf("billing", "books", "business", "backup"), tagSuggestionsFor(input = "b", allTags = all, tags = listOf("banking")), ) } @Test fun tag_normalization_allows_spaces_and_caps_length_like_browser() { assertEquals("shared login", normalizeTag(" Shared Login ")) assertEquals("a".repeat(MAX_TAG_LEN), normalizeTag("A".repeat(MAX_TAG_LEN + 8))) } // ── Card field formatting (CardFieldUtils, used live by setCardNumber etc.) ─ @Test fun new_card_number_auto_formats() { assertEquals("4242 4242 4242 4242", CardFieldUtils.formatPan("4242424242424242")) // Amex groups 4-6-5. assertEquals("3782 822463 10005", CardFieldUtils.formatPan("378282246310005")) } @Test fun card_network_detection_matches_browser_add_view_badges() { assertEquals("visa", CardFieldUtils.detectNetwork("4242424242424242")) assertEquals("mastercard", CardFieldUtils.detectNetwork("5555555555554444")) assertEquals("mastercard", CardFieldUtils.detectNetwork("2221000000000009")) assertEquals("amex", CardFieldUtils.detectNetwork("378282246310005")) assertEquals("discover", CardFieldUtils.detectNetwork("6011111111111117")) assertEquals("discover", CardFieldUtils.detectNetwork("6500000000000002")) assertEquals("unknown", CardFieldUtils.detectNetwork("6000000000000000")) } @Test fun new_card_expiry_auto_formats_browser_mm_year() { assertEquals("12", CardFieldUtils.formatExpiry("12")) assertEquals("12/28", CardFieldUtils.formatExpiry("1228")) assertEquals("12/2899", CardFieldUtils.formatExpiry("122899")) // caps year at 4 digits assertEquals("12", CardFieldUtils.expiryMonth("12/2028")) assertEquals("2028", CardFieldUtils.expiryYear("12/2028")) assertEquals("12/2028", CardFieldUtils.combineExpiry("12", "2028")) } @Test fun new_card_expiry_rejects_invalid_month() { // currentYear2 = 26, currentMonth = 6 (2026-06). assertFalse(CardFieldUtils.isExpiryValid("13/28", currentYear2 = 26, currentMonth = 6)) assertFalse(CardFieldUtils.isExpiryValid("00/28", currentYear2 = 26, currentMonth = 6)) assertTrue(CardFieldUtils.isExpiryValid("12/28", currentYear2 = 26, currentMonth = 6)) assertTrue(CardFieldUtils.isExpiryValid("12/2028", currentYear2 = 26, currentMonth = 6)) // Past year rejected. assertFalse(CardFieldUtils.isExpiryValid("12/24", currentYear2 = 26, currentMonth = 6)) } private fun identityEntry(id: String, username: String?, email: String?) = VaultEntry( id = id, title = id, type = "login", username = username, email = email, createdAt = 0, modifiedAt = 0, ) }