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 Entry creates logins/cards; Notes mode creates notes ─────────────
@Test fun new_entry_type_picker_excludes_notes() {
assertEquals(listOf("login", "card"), entryEditTypeSegments(isNew = true).map { it.key })
assertEquals(listOf("login", "note", "card"), entryEditTypeSegments(isNew = false).map { it.key })
}
// ── 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 = ""))
assertFalse(shouldShowLoginNotesLink(isNew = true, type = "login", revealed = false, notes = ""))
assertTrue(shouldShowLoginNotesLink(isNew = false, type = "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,
)
}