AutofillPreviewLabelsTest.kt

6.9 KB · raw

1package com.peachpasswords.autofill23import com.peachpasswords.autofill.AutofillResponseBuilder.FillMode4import com.peachpasswords.vault.VaultEntry5import org.junit.Assert.assertEquals6import org.junit.Assert.assertNotNull7import org.junit.Test89class AutofillPreviewLabelsTest {10    private fun entry(11        title: String = "GitHub",12        username: String? = "june",13        email: String? = null,14        url: String? = "https://github.com",15    ) = VaultEntry(16        id = title, title = title, username = username, email = email, url = url,17        createdAt = 0, modifiedAt = 0,18    )1920    @Test fun normal_mode_shows_entry_title_and_identity() {21        val labels = AutofillResponseBuilder.previewLabels(false, entry(), "github.com")22        assertEquals("GitHub", labels.title)23        assertEquals("june", labels.subtitle)24        assertEquals(FillMode.LOGIN, labels.fillMode)25    }2627    @Test fun normal_mode_falls_back_email_then_domain() {28        val emailOnly = entry(username = null, email = "[email protected]")29        assertEquals("[email protected]", AutofillResponseBuilder.previewLabels(false, emailOnly, null).subtitle)30        val bare = entry(username = null, email = null)31        assertEquals("github.com", AutofillResponseBuilder.previewLabels(false, bare, null).subtitle)32    }3334    @Test fun private_mode_hides_title_and_identity() {35        val labels = AutofillResponseBuilder.previewLabels(true, entry(), "github.com")36        assertEquals("Peach", labels.title)37        assertEquals("github.com", labels.subtitle)38    }3940    @Test fun private_mode_without_origin_has_blank_subtitle() {41        val labels = AutofillResponseBuilder.previewLabels(true, entry(), null)42        assertEquals("Peach", labels.title)43        assertEquals("", labels.subtitle)44    }4546    // --- OTP_FILL fillMode: the chip surfaces the actual TOTP code ---4748    @Test fun otpFill_mode_shows_chunked_code_as_title_and_entry_as_subtitle() {49        val labels = AutofillResponseBuilder.previewLabels(50            privatePreview = false,51            entry = entry(),52            originDomain = "github.com",53            fillMode = FillMode.OTP_FILL,54            otpCode = "482901",55        )56        assertEquals("482 901", labels.title)57        assertEquals("2FA", labels.subtitle)58        assertEquals("GitHub", labels.iconSource)59        assertEquals(FillMode.OTP_FILL, labels.fillMode)60    }6162    @Test fun otpFill_mode_passes_through_long_codes_unchunked() {63        val labels = AutofillResponseBuilder.previewLabels(64            privatePreview = false,65            entry = entry(),66            originDomain = null,67            fillMode = FillMode.OTP_FILL,68            otpCode = "JBSWY3DPEHPK3PXP",69        )70        // 8-char Steam-style code — chunk only fires on <=6 digits, this passes through.71        assertEquals("JBSWY3DPEHPK3PXP", labels.title)72        assertEquals("2FA", labels.subtitle)73    }7475    @Test fun otpFill_mode_never_leaks_digits_in_private_preview() {76        val labels = AutofillResponseBuilder.previewLabels(77            privatePreview = true,78            entry = entry(),79            originDomain = "github.com",80            fillMode = FillMode.OTP_FILL,81            otpCode = "482901",82        )83        // Privacy always wins over fillMode — chip says "Peach" not the digits.84        assertEquals("Peach", labels.title)85        assertEquals("github.com", labels.subtitle)86    }8788    @Test(expected = IllegalArgumentException::class)89    fun otpFill_mode_requires_otpCode() {90        AutofillResponseBuilder.previewLabels(91            privatePreview = false,92            entry = entry(),93            originDomain = "github.com",94            fillMode = FillMode.OTP_FILL,95            otpCode = null,96        )97    }9899    @Test fun chunkOtpForDisplay_groups_only_6digit_codes_with_space() {100        // 6-digit only: grouped into 3+3.101        assertEquals("482 901", AutofillResponseBuilder.chunkOtpForDisplay("482901"))102        // 5-digit and 7-digit: pass through (no sane chunking).103        assertEquals("12345", AutofillResponseBuilder.chunkOtpForDisplay("12345"))104        assertEquals("1234567", AutofillResponseBuilder.chunkOtpForDisplay("1234567"))105        // Alphanumeric (Steam-style base32): pass through unchanged — never strip letters.106        assertEquals("JBSWY3DPEHPK3PXP", AutofillResponseBuilder.chunkOtpForDisplay("JBSWY3DPEHPK3PXP"))107    }108}109110// --- Countdown subtitle on the 2FA chip ---111112class AutofillPreviewLabelsCountdownTest {113    private fun entry(title: String = "GitHub") = VaultEntry(114        id = title, title = title, username = "june",115        createdAt = 0, modifiedAt = 0,116    )117118    @Test fun otpFill_chip_counts_down_seconds_remaining_in_subtitle() {119        val labels = AutofillResponseBuilder.previewLabels(120            privatePreview = false,121            entry = entry(),122            originDomain = "github.com",123            fillMode = FillMode.OTP_FILL,124            otpCode = "482901",125            secondsRemaining = 23,126        )127        assertEquals("482 901", labels.title)128        assertEquals("2FA · 23s", labels.subtitle)129    }130131    @Test fun otpFill_chip_amber_zone_at_five_to_nine_seconds() {132        val labels = AutofillResponseBuilder.previewLabels(133            false, entry(), null,134            fillMode = FillMode.OTP_FILL, otpCode = "123456", secondsRemaining = 7,135        )136        assertEquals("2FA · 7s", labels.subtitle)137    }138139    @Test fun otpFill_chip_red_zone_at_or_below_four_seconds() {140        val labels = AutofillResponseBuilder.previewLabels(141            false, entry(), null,142            fillMode = FillMode.OTP_FILL, otpCode = "123456", secondsRemaining = 3,143        )144        assertEquals("2FA · 3s", labels.subtitle)145    }146147    @Test fun otpFill_chip_at_zero_or_below_shows_expired() {148        val labels = AutofillResponseBuilder.previewLabels(149            false, entry(), null,150            fillMode = FillMode.OTP_FILL, otpCode = "123456", secondsRemaining = 0,151        )152        assertEquals("2FA · expired", labels.subtitle)153    }154155    @Test fun otpFill_chip_without_secondsRemaining_keeps_legacy_subtitle() {156        // Back-compat: callers that don't pass secondsRemaining get the same157        // label as v2.14.4. No end-icon either — purely visual change gated158        // on the freshness signal.159        val labels = AutofillResponseBuilder.previewLabels(160            false, entry(), "github.com",161            fillMode = FillMode.OTP_FILL, otpCode = "482901",162        )163        assertEquals("2FA", labels.subtitle)164        assertEquals(null, labels.endIcon)165    }166167    @Test fun private_mode_still_hides_digits_even_with_countdown() {168        // Privacy wins: "Peach" not the digits, no countdown leak either.169        val labels = AutofillResponseBuilder.previewLabels(170            true, entry(), "github.com",171            fillMode = FillMode.OTP_FILL, otpCode = "482901", secondsRemaining = 7,172        )173        assertEquals("Peach", labels.title)174        assertEquals("github.com", labels.subtitle)175    }176}