diff --git a/authenticator-screenshots/.gitignore b/authenticator-screenshots/.gitignore
deleted file mode 100644
index 42afabfd..00000000
--- a/authenticator-screenshots/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-/build
\ No newline at end of file
diff --git a/authenticator-screenshots/README.md b/authenticator-screenshots/README.md
deleted file mode 100644
index cd657c05..00000000
--- a/authenticator-screenshots/README.md
+++ /dev/null
@@ -1,15 +0,0 @@
-# Authenticator Screenshots
-
-Generates screenshots for the [Authenticator](../authenticator) project.
-
-```shell
-# Record screenshots
-./gradlew authenticator-screenshots:recordPaparazziDebug
-
-# Verify screenshots
-./gradlew authenticator-screenshots:verifyPaparazziDebug
-```
-
-## Why a separate module?
-
-Paparazzi currently [has an issue](https://github.com/cashapp/paparazzi/issues/622) that prevents it from co-habitating with Robolectric tests.
diff --git a/authenticator-screenshots/build.gradle.kts b/authenticator-screenshots/build.gradle.kts
deleted file mode 100644
index a0373ea9..00000000
--- a/authenticator-screenshots/build.gradle.kts
+++ /dev/null
@@ -1,42 +0,0 @@
-/*
- * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-plugins {
- id("amplify.android.library")
- alias(libs.plugins.paparazzi)
-}
-
-android {
- namespace = "com.amplifyframework.ui.authenticator.screenshots"
-
- compileOptions {
- isCoreLibraryDesugaringEnabled = true
- }
-}
-
-dependencies {
- implementation(platform(libs.androidx.compose.bom))
-
- implementation(libs.bundles.compose)
- implementation(libs.test.mockk)
- implementation(projects.authenticator)
-
- coreLibraryDesugaring(libs.android.desugar)
-}
-
-// Verify screenshots when running the check task
-tasks.named("check").configure {
- dependsOn(tasks.first { it.name == "verifyPaparazzi" })
-}
diff --git a/authenticator-screenshots/src/main/AndroidManifest.xml b/authenticator-screenshots/src/main/AndroidManifest.xml
deleted file mode 100644
index dbd6e50d..00000000
--- a/authenticator-screenshots/src/main/AndroidManifest.xml
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-
-
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/MockAuthenticatorData.kt b/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/MockAuthenticatorData.kt
deleted file mode 100644
index ce00f61e..00000000
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/MockAuthenticatorData.kt
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.amplifyframework.ui.authenticator
-
-import com.amplifyframework.ui.authenticator.forms.FieldConfig
-import com.amplifyframework.ui.authenticator.forms.FieldError
-import com.amplifyframework.ui.authenticator.forms.FieldValidator
-import com.amplifyframework.ui.authenticator.forms.MutableFieldData
-import com.amplifyframework.ui.authenticator.forms.MutableFieldState
-import com.amplifyframework.ui.authenticator.forms.MutableFormState
-import com.amplifyframework.ui.authenticator.forms.MutablePasswordFieldState
-
-fun mockForm(
- vararg fields: MutableFieldData,
- enabled: Boolean = true
-) = object : MutableFormState {
- override val fields = fields.associateBy { it.config.key }
- override var enabled = enabled
-}
-
-fun mockFieldData(
- config: FieldConfig,
- state: MutableFieldState = mockFieldState(),
- validator: FieldValidator = { null }
-) = object : MutableFieldData {
- override val state = state
- override val config = config
- override val validator = validator
-}
-
-fun mockFieldState(
- content: String = "",
- error: FieldError? = null
-) = object : MutableFieldState {
- override var content = content
- override var error = error
-}
-
-fun mockPasswordFieldState(
- content: String = "",
- error: FieldError? = null,
- visible: Boolean = false
-) = object : MutablePasswordFieldState {
- override var fieldContentVisible = visible
- override var content = content
- override var error: FieldError? = error
-}
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/PasswordResetScreenshots.kt b/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/PasswordResetScreenshots.kt
deleted file mode 100644
index 2bcfba58..00000000
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/PasswordResetScreenshots.kt
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.amplifyframework.ui.authenticator.ui
-
-import com.amplifyframework.ui.authenticator.PasswordResetState
-import com.amplifyframework.ui.authenticator.ScreenshotTestBase
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.forms.FieldConfig
-import com.amplifyframework.ui.authenticator.forms.FieldError
-import com.amplifyframework.ui.authenticator.forms.FieldError.NotFound
-import com.amplifyframework.ui.authenticator.forms.FieldKey
-import com.amplifyframework.ui.authenticator.forms.MutableFormState
-import com.amplifyframework.ui.authenticator.mockFieldData
-import com.amplifyframework.ui.authenticator.mockFieldState
-import com.amplifyframework.ui.authenticator.mockForm
-import org.junit.Test
-
-class PasswordResetScreenshots : ScreenshotTestBase() {
-
- @Test
- fun default_state() {
- screenshot {
- PasswordReset(state = mockPasswordResetState())
- }
- }
-
- @Test
- fun username_not_found() {
- screenshot {
- PasswordReset(state = mockPasswordResetState(error = NotFound))
- }
- }
-
- private fun mockPasswordResetState(
- error: FieldError? = null
- ) = object : PasswordResetState {
- override fun moveTo(step: AuthenticatorInitialStep) {}
- override suspend fun submitPasswordReset() {}
- override val form: MutableFormState = mockForm(
- mockFieldData(FieldConfig.Text(FieldKey.Username), state = mockFieldState(error = error))
- )
- override val step = AuthenticatorStep.PasswordReset
- }
-}
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmMfa.kt b/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmMfa.kt
deleted file mode 100644
index 6f2b2957..00000000
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmMfa.kt
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.amplifyframework.ui.authenticator.ui
-
-import com.amplifyframework.auth.AuthCodeDeliveryDetails
-import com.amplifyframework.ui.authenticator.ScreenshotTestBase
-import com.amplifyframework.ui.authenticator.SignInConfirmMfaState
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.forms.FieldConfig
-import com.amplifyframework.ui.authenticator.forms.FieldError
-import com.amplifyframework.ui.authenticator.forms.FieldKey
-import com.amplifyframework.ui.authenticator.mockFieldData
-import com.amplifyframework.ui.authenticator.mockFieldState
-import com.amplifyframework.ui.authenticator.mockForm
-import org.junit.Test
-
-class SignInConfirmMfa : ScreenshotTestBase() {
-
- @Test
- fun sign_in_confirm_email_mfa_default() {
- screenshot {
- SignInConfirmMfa(
- mockSignInConfirmMfaState(
- AuthCodeDeliveryDetails(
- "email@email.com",
- AuthCodeDeliveryDetails.DeliveryMedium.EMAIL
- )
- )
- )
- }
- }
-
- @Test
- fun sign_in_confirm_email_mfa_incorrect_code() {
- screenshot {
- SignInConfirmMfa(
- mockSignInConfirmMfaState(
- AuthCodeDeliveryDetails(
- "email@email.com",
- AuthCodeDeliveryDetails.DeliveryMedium.EMAIL
- ),
- "123456",
- FieldError.ConfirmationCodeIncorrect
- )
- )
- }
- }
-
- @Test
- fun sign_in_confirm_sms_mfa_default() {
- screenshot {
- SignInConfirmMfa(
- mockSignInConfirmMfaState(
- AuthCodeDeliveryDetails(
- "123-123-1234",
- AuthCodeDeliveryDetails.DeliveryMedium.SMS
- )
- )
- )
- }
- }
-
- @Test
- fun sign_in_confirm_sms_mfa_incorrect_code() {
- screenshot {
- SignInConfirmMfa(
- mockSignInConfirmMfaState(
- AuthCodeDeliveryDetails(
- "123-123-1234",
- AuthCodeDeliveryDetails.DeliveryMedium.SMS
- ),
- "123456",
- FieldError.ConfirmationCodeIncorrect
- )
- )
- }
- }
-
- private fun mockSignInConfirmMfaState(
- deliveryDetails: AuthCodeDeliveryDetails,
- confirmationCode: String = "",
- fieldError: FieldError? = null
- ) = object : SignInConfirmMfaState {
- override val form = mockForm(
- mockFieldData(
- config = FieldConfig.Text(FieldKey.ConfirmationCode),
- state = mockFieldState(content = confirmationCode, error = fieldError)
- )
- )
- override val deliveryDetails: AuthCodeDeliveryDetails
- get() = deliveryDetails
-
- override fun moveTo(step: AuthenticatorInitialStep) {}
- override suspend fun confirmSignIn() {
- TODO("Not yet implemented")
- }
-
- override val step = AuthenticatorStep.SignInContinueWithEmailSetup
- }
-}
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmTotpCodeScreenshots.kt b/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmTotpCodeScreenshots.kt
deleted file mode 100644
index 61012e3a..00000000
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmTotpCodeScreenshots.kt
+++ /dev/null
@@ -1,63 +0,0 @@
-/*
- * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.amplifyframework.ui.authenticator.ui
-
-import com.amplifyframework.ui.authenticator.ScreenshotTestBase
-import com.amplifyframework.ui.authenticator.SignInConfirmTotpCodeState
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.forms.FieldConfig
-import com.amplifyframework.ui.authenticator.forms.FieldError
-import com.amplifyframework.ui.authenticator.forms.FieldKey
-import com.amplifyframework.ui.authenticator.mockFieldData
-import com.amplifyframework.ui.authenticator.mockFieldState
-import com.amplifyframework.ui.authenticator.mockForm
-import org.junit.Test
-
-class SignInConfirmTotpCodeScreenshots : ScreenshotTestBase() {
-
- @Test
- fun default_state() {
- screenshot {
- SignInConfirmTotpCode(state = mockSignInConfirmTotpCodeState())
- }
- }
-
- @Test
- fun invalid_code() {
- screenshot {
- SignInConfirmTotpCode(
- state = mockSignInConfirmTotpCodeState(fieldError = FieldError.ConfirmationCodeIncorrect)
- )
- }
- }
-
- private fun mockSignInConfirmTotpCodeState(
- confirmationCode: String = "",
- fieldError: FieldError? = null
- ) = object : SignInConfirmTotpCodeState {
- override val form = mockForm(
- mockFieldData(
- config = FieldConfig.Text(FieldKey.ConfirmationCode),
- state = mockFieldState(content = confirmationCode, error = fieldError)
- )
- )
-
- override fun moveTo(step: AuthenticatorInitialStep) {}
- override suspend fun confirmSignIn() {}
- override val step = AuthenticatorStep.SignInConfirmTotpCode
- }
-}
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithEmailSetupScreenshots.kt b/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithEmailSetupScreenshots.kt
deleted file mode 100644
index bceb39af..00000000
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithEmailSetupScreenshots.kt
+++ /dev/null
@@ -1,49 +0,0 @@
-/*
- * Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.amplifyframework.ui.authenticator.ui
-
-import com.amplifyframework.ui.authenticator.ScreenshotTestBase
-import com.amplifyframework.ui.authenticator.SignInContinueWithEmailSetupState
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.forms.FieldConfig
-import com.amplifyframework.ui.authenticator.forms.FieldKey
-import com.amplifyframework.ui.authenticator.mockFieldData
-import com.amplifyframework.ui.authenticator.mockFieldState
-import com.amplifyframework.ui.authenticator.mockForm
-import org.junit.Test
-
-class SignInContinueWithEmailSetupScreenshots : ScreenshotTestBase() {
-
- @Test
- fun default_state() {
- screenshot {
- SignInContinueWithEmailSetup(mockSignInContinueWithEmailSetupState())
- }
- }
-
- private fun mockSignInContinueWithEmailSetupState() = object : SignInContinueWithEmailSetupState {
- override val form = mockForm(
- mockFieldData(
- config = FieldConfig.Text(FieldKey.Email),
- state = mockFieldState(content = "email@email.com")
- )
- )
- override fun moveTo(step: AuthenticatorInitialStep) {}
- override suspend fun continueSignIn() {}
- override val step = AuthenticatorStep.SignInContinueWithEmailSetup
- }
-}
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionScreenshots.kt b/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionScreenshots.kt
deleted file mode 100644
index f6ea55ee..00000000
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionScreenshots.kt
+++ /dev/null
@@ -1,36 +0,0 @@
-package com.amplifyframework.ui.authenticator.ui
-
-import com.amplifyframework.auth.MFAType
-import com.amplifyframework.ui.authenticator.ScreenshotTestBase
-import com.amplifyframework.ui.authenticator.SignInContinueWithMfaSelectionState
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.forms.FieldConfig
-import com.amplifyframework.ui.authenticator.forms.FieldKey
-import com.amplifyframework.ui.authenticator.mockFieldData
-import com.amplifyframework.ui.authenticator.mockFieldState
-import com.amplifyframework.ui.authenticator.mockForm
-import org.junit.Test
-
-class SignInContinueWithMfaSelectionScreenshots : ScreenshotTestBase() {
-
- @Test
- fun default_state() {
- screenshot {
- SignInContinueWithMfaSelection(mockSignInContinueWithMfaSelectionState())
- }
- }
-
- private fun mockSignInContinueWithMfaSelectionState() = object : SignInContinueWithMfaSelectionState {
- override val form = mockForm(
- mockFieldData(
- config = FieldConfig.Text(FieldKey.MfaSelection),
- state = mockFieldState(content = "SMS_MFA")
- )
- )
- override val allowedMfaTypes = MFAType.values().toSet()
- override fun moveTo(step: AuthenticatorInitialStep) {}
- override suspend fun continueSignIn() {}
- override val step = AuthenticatorStep.SignInContinueWithMfaSelection
- }
-}
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSetupSelection.kt b/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSetupSelection.kt
deleted file mode 100644
index acc9b433..00000000
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSetupSelection.kt
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright 2024 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.amplifyframework.ui.authenticator.ui
-
-import com.amplifyframework.auth.MFAType
-import com.amplifyframework.ui.authenticator.ScreenshotTestBase
-import com.amplifyframework.ui.authenticator.SignInContinueWithMfaSetupSelectionState
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.forms.FieldConfig
-import com.amplifyframework.ui.authenticator.forms.FieldKey
-import com.amplifyframework.ui.authenticator.mockFieldData
-import com.amplifyframework.ui.authenticator.mockFieldState
-import com.amplifyframework.ui.authenticator.mockForm
-import org.junit.Test
-
-class SignInContinueWithMfaSetupSelection : ScreenshotTestBase() {
-
- @Test
- fun default_state() {
- screenshot {
- SignInContinueWithMfaSetupSelection(mockSignInContinueWithMfaSetupSelectionState())
- }
- }
-
- private fun mockSignInContinueWithMfaSetupSelectionState() = object : SignInContinueWithMfaSetupSelectionState {
- override val form = mockForm(
- mockFieldData(
- config = FieldConfig.Text(FieldKey.MfaSelection),
- state = mockFieldState(content = "EMAIL_OTP")
- )
- )
- override val allowedMfaTypes: Set
- get() = setOf(MFAType.TOTP, MFAType.SMS, MFAType.EMAIL)
-
- override fun moveTo(step: AuthenticatorInitialStep) {}
- override suspend fun continueSignIn() {}
- override val step = AuthenticatorStep.SignInContinueWithEmailSetup
- }
-}
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithTotpSetupScreenshots.kt b/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithTotpSetupScreenshots.kt
deleted file mode 100644
index cff46c92..00000000
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithTotpSetupScreenshots.kt
+++ /dev/null
@@ -1,54 +0,0 @@
-package com.amplifyframework.ui.authenticator.ui
-
-import com.amplifyframework.ui.authenticator.ScreenshotTestBase
-import com.amplifyframework.ui.authenticator.SignInContinueWithTotpSetupState
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.forms.FieldConfig
-import com.amplifyframework.ui.authenticator.forms.FieldError
-import com.amplifyframework.ui.authenticator.forms.FieldKey
-import com.amplifyframework.ui.authenticator.mockFieldData
-import com.amplifyframework.ui.authenticator.mockFieldState
-import com.amplifyframework.ui.authenticator.mockForm
-import org.junit.Test
-
-class SignInContinueWithTotpSetupScreenshots : ScreenshotTestBase() {
-
- @Test
- fun default_state() {
- screenshot {
- SignInContinueWithTotpSetup(state = mockSignInContinueWithTotpSetupState())
- }
- }
-
- @Test
- fun invalid_code() {
- screenshot {
- SignInContinueWithTotpSetup(
- state = mockSignInContinueWithTotpSetupState(
- confirmationCode = "123456",
- fieldError = FieldError.ConfirmationCodeIncorrect
- )
- )
- }
- }
-
- private fun mockSignInContinueWithTotpSetupState(
- confirmationCode: String = "",
- sharedSecret: String = "secret",
- setupUri: String = "https://docs.amplify.aws/android/tools/libraries/",
- fieldError: FieldError? = null
- ) = object : SignInContinueWithTotpSetupState {
- override val form = mockForm(
- mockFieldData(
- config = FieldConfig.Text(FieldKey.ConfirmationCode),
- state = mockFieldState(content = confirmationCode, error = fieldError)
- )
- )
- override val sharedSecret = sharedSecret
- override val setupUri = setupUri
- override fun moveTo(step: AuthenticatorInitialStep) {}
- override suspend fun continueSignIn() {}
- override val step = AuthenticatorStep.SignInContinueWithTotpSetup
- }
-}
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInScreenshots.kt b/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInScreenshots.kt
deleted file mode 100644
index b4fe563f..00000000
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInScreenshots.kt
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.amplifyframework.ui.authenticator.ui
-
-import com.amplifyframework.ui.authenticator.ScreenshotTestBase
-import com.amplifyframework.ui.authenticator.SignInState
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.forms.FieldConfig
-import com.amplifyframework.ui.authenticator.forms.FieldError
-import com.amplifyframework.ui.authenticator.forms.FieldKey
-import com.amplifyframework.ui.authenticator.mockFieldData
-import com.amplifyframework.ui.authenticator.mockFieldState
-import com.amplifyframework.ui.authenticator.mockForm
-import com.amplifyframework.ui.authenticator.mockPasswordFieldState
-import org.junit.Test
-
-class SignInScreenshots : ScreenshotTestBase() {
-
- @Test
- fun default_state() {
- screenshot {
- SignIn(state = mockSignInState())
- }
- }
-
- @Test
- fun ready_to_submit() {
- screenshot {
- SignIn(state = mockSignInState(username = "username", password = "password"))
- }
- }
-
- @Test
- fun password_visible() {
- screenshot {
- SignIn(state = mockSignInState(username = "username", password = "password", passwordVisible = true))
- }
- }
-
- @Test
- fun username_not_found() {
- screenshot {
- SignIn(state = mockSignInState(username = "username", usernameError = FieldError.NotFound))
- }
- }
-
- private fun mockSignInState(
- username: String = "",
- password: String = "",
- usernameError: FieldError? = null,
- passwordVisible: Boolean = false
- ) = object : SignInState {
- override fun moveTo(step: AuthenticatorInitialStep) {}
- override suspend fun signIn() {}
- override val form = mockForm(
- mockFieldData(
- config = FieldConfig.Text(FieldKey.Username),
- state = mockFieldState(content = username, error = usernameError)
- ),
- mockFieldData(
- FieldConfig.Password(FieldKey.Password),
- state = mockPasswordFieldState(content = password, visible = passwordVisible)
- )
- )
- override val step = AuthenticatorStep.SignIn
- }
-}
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignUpScreenshots.kt b/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignUpScreenshots.kt
deleted file mode 100644
index 717a8c5c..00000000
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ui/SignUpScreenshots.kt
+++ /dev/null
@@ -1,178 +0,0 @@
-/*
- * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
- *
- * Licensed under the Apache License, Version 2.0 (the "License").
- * You may not use this file except in compliance with the License.
- * A copy of the License is located at
- *
- * http://aws.amazon.com/apache2.0
- *
- * or in the "license" file accompanying this file. This file is distributed
- * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
- * express or implied. See the License for the specific language governing
- * permissions and limitations under the License.
- */
-
-package com.amplifyframework.ui.authenticator.ui
-
-import com.amplifyframework.ui.authenticator.ScreenshotTestBase
-import com.amplifyframework.ui.authenticator.SignUpState
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
-import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.forms.FieldConfig
-import com.amplifyframework.ui.authenticator.forms.FieldError
-import com.amplifyframework.ui.authenticator.forms.FieldKey
-import com.amplifyframework.ui.authenticator.forms.PasswordError
-import com.amplifyframework.ui.authenticator.mockFieldData
-import com.amplifyframework.ui.authenticator.mockFieldState
-import com.amplifyframework.ui.authenticator.mockForm
-import com.amplifyframework.ui.authenticator.mockPasswordFieldState
-import io.mockk.every
-import io.mockk.mockk
-import org.junit.Test
-
-class SignUpScreenshots : ScreenshotTestBase() {
-
- @Test
- fun default_state() {
- screenshot {
- SignUp(state = mockSignUpState())
- }
- }
-
- @Test
- fun ready_to_submit() {
- screenshot {
- SignUp(
- state = mockSignUpState(
- username = "username",
- password = "password",
- confirmPassword = "password",
- email = "email@email.com"
- )
- )
- }
- }
-
- @Test
- fun password_visible() {
- screenshot {
- SignUp(
- state = mockSignUpState(
- username = "username",
- password = "password",
- confirmPassword = "password",
- email = "email@email.com",
- passwordVisible = true
- )
- )
- }
- }
-
- @Test
- fun username_exists() {
- screenshot {
- SignUp(
- state = mockSignUpState(
- username = "username",
- password = "password",
- confirmPassword = "password",
- email = "email@email.com",
- usernameError = FieldError.FieldValueExists
- )
- )
- }
- }
-
- @Test
- fun invalid_password() {
- val error = mockk {
- every { errors } returns listOf(
- PasswordError.InvalidPasswordLength(10),
- PasswordError.InvalidPasswordMissingUpper,
- PasswordError.InvalidPasswordMissingSpecial,
- PasswordError.InvalidPasswordMissingNumber
- )
- }
-
- screenshot {
- SignUp(
- state = mockSignUpState(
- username = "username",
- password = "password",
- confirmPassword = "password",
- email = "email@email.com",
- passwordError = error
- )
- )
- }
- }
-
- @Test
- fun passwords_do_not_match() {
- screenshot {
- SignUp(
- state = mockSignUpState(
- username = "username",
- password = "password",
- confirmPassword = "password2",
- email = "email@email.com",
- confirmPasswordError = FieldError.PasswordsDoNotMatch
- )
- )
- }
- }
-
- @Test
- fun invalid_email() {
- screenshot {
- SignUp(
- state = mockSignUpState(
- username = "username",
- password = "password",
- confirmPassword = "password2",
- email = "email@email.com",
- emailError = FieldError.InvalidFormat
- )
- )
- }
- }
-
- private fun mockSignUpState(
- username: String = "",
- password: String = "",
- confirmPassword: String = "",
- email: String = "",
- usernameError: FieldError? = null,
- passwordError: FieldError? = null,
- confirmPasswordError: FieldError? = null,
- emailError: FieldError? = null,
- passwordVisible: Boolean = false
- ) = object : SignUpState {
- override fun moveTo(step: AuthenticatorInitialStep) {}
- override suspend fun signUp() {}
- override val form = mockForm(
- mockFieldData(
- config = FieldConfig.Text(FieldKey.Username),
- state = mockFieldState(content = username, error = usernameError)
- ),
- mockFieldData(
- config = FieldConfig.Password(FieldKey.Password),
- state = mockPasswordFieldState(content = password, error = passwordError, visible = passwordVisible)
- ),
- mockFieldData(
- config = FieldConfig.Password(FieldKey.ConfirmPassword),
- state = mockPasswordFieldState(
- content = confirmPassword,
- error = confirmPasswordError,
- visible = passwordVisible
- )
- ),
- mockFieldData(
- config = FieldConfig.Text(FieldKey.Email),
- state = mockFieldState(content = email, error = emailError)
- )
- )
- override val step = AuthenticatorStep.SignUp
- }
-}
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_PasswordResetScreenshots_default_state.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_PasswordResetScreenshots_default_state.png
deleted file mode 100644
index 815f187d..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_PasswordResetScreenshots_default_state.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_PasswordResetScreenshots_username_not_found.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_PasswordResetScreenshots_username_not_found.png
deleted file mode 100644
index 579fb7c2..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_PasswordResetScreenshots_username_not_found.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_email_mfa_default.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_email_mfa_default.png
deleted file mode 100644
index 8ff47960..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_email_mfa_default.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_email_mfa_incorrect_code.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_email_mfa_incorrect_code.png
deleted file mode 100644
index 858f28c4..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_email_mfa_incorrect_code.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_sms_mfa_default.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_sms_mfa_default.png
deleted file mode 100644
index 2a6f2d18..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_sms_mfa_default.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_sms_mfa_incorrect_code.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_sms_mfa_incorrect_code.png
deleted file mode 100644
index aa3eaef8..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmMfa_sign_in_confirm_sms_mfa_incorrect_code.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmTotpCodeScreenshots_default_state.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmTotpCodeScreenshots_default_state.png
deleted file mode 100644
index 8579db2f..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmTotpCodeScreenshots_default_state.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmTotpCodeScreenshots_invalid_code.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmTotpCodeScreenshots_invalid_code.png
deleted file mode 100644
index b6fac089..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInConfirmTotpCodeScreenshots_invalid_code.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithEmailSetupScreenshots_default_state.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithEmailSetupScreenshots_default_state.png
deleted file mode 100644
index 7608db24..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithEmailSetupScreenshots_default_state.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithMfaSelectionScreenshots_default_state.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithMfaSelectionScreenshots_default_state.png
deleted file mode 100644
index 0d58a407..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithMfaSelectionScreenshots_default_state.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithMfaSetupSelection_default_state.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithMfaSetupSelection_default_state.png
deleted file mode 100644
index 7c5bcf25..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithMfaSetupSelection_default_state.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithTotpSetupScreenshots_default_state.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithTotpSetupScreenshots_default_state.png
deleted file mode 100644
index f7f7d8c9..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithTotpSetupScreenshots_default_state.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithTotpSetupScreenshots_invalid_code.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithTotpSetupScreenshots_invalid_code.png
deleted file mode 100644
index 91b362b4..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInContinueWithTotpSetupScreenshots_invalid_code.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_default_state.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_default_state.png
deleted file mode 100644
index b4e35cb7..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_default_state.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_password_visible.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_password_visible.png
deleted file mode 100644
index d0e3ebbc..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_password_visible.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_ready_to_submit.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_ready_to_submit.png
deleted file mode 100644
index 23c0f8a2..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_ready_to_submit.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_username_not_found.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_username_not_found.png
deleted file mode 100644
index 3b917146..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignInScreenshots_username_not_found.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_default_state.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_default_state.png
deleted file mode 100644
index cd3d4523..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_default_state.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_invalid_email.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_invalid_email.png
deleted file mode 100644
index ecea9c30..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_invalid_email.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_invalid_password.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_invalid_password.png
deleted file mode 100644
index 5cddfdf7..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_invalid_password.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_password_visible.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_password_visible.png
deleted file mode 100644
index fa5564eb..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_password_visible.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_passwords_do_not_match.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_passwords_do_not_match.png
deleted file mode 100644
index d1e8aa24..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_passwords_do_not_match.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_ready_to_submit.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_ready_to_submit.png
deleted file mode 100644
index b6063a86..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_ready_to_submit.png and /dev/null differ
diff --git a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_username_exists.png b/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_username_exists.png
deleted file mode 100644
index 0c128822..00000000
Binary files a/authenticator-screenshots/src/test/snapshots/images/com.amplifyframework.ui.authenticator.ui_SignUpScreenshots_username_exists.png and /dev/null differ
diff --git a/authenticator/gradle.properties b/authenticator/gradle.properties
index 270e0f28..cab47cce 100644
--- a/authenticator/gradle.properties
+++ b/authenticator/gradle.properties
@@ -17,4 +17,4 @@ POM_ARTIFACT_ID=authenticator
POM_NAME=Amplify UI Framework for Android - Authenticator
POM_DESCRIPTION=Amplify UI Framework for Android - Authenticator Plugin
POM_PACKAGING=aar
-VERSION_NAME=1.5.0
+VERSION_NAME=1.5.0
\ No newline at end of file
diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/AuthenticatorTitle.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/AuthenticatorTitle.kt
index d9abb448..a44b31cd 100644
--- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/AuthenticatorTitle.kt
+++ b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/AuthenticatorTitle.kt
@@ -34,6 +34,6 @@ internal fun AuthenticatorTitle(
Text(
style = MaterialTheme.typography.titleLarge,
text = text,
- modifier = modifier.padding(bottom = 16.dp).testTag("AuthenticatorTitle")
+ modifier = modifier.padding(bottom = 16.dp).testTag(TestTags.AuthenticatorTitle)
)
}
diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/PasswordInputField.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/PasswordInputField.kt
index 19065e38..8483a41a 100644
--- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/PasswordInputField.kt
+++ b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/PasswordInputField.kt
@@ -26,6 +26,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusDirection
import androidx.compose.ui.platform.LocalFocusManager
+import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.input.ImeAction
@@ -91,7 +92,7 @@ private fun getTrailingIcon(visible: Boolean, onClick: () -> Unit): @Composable
true -> R.string.amplify_ui_authenticator_field_a11y_password_hide
false -> R.string.amplify_ui_authenticator_field_a11y_password_show
}
- IconButton(onClick = onClick) {
+ IconButton(onClick = onClick, modifier = Modifier.testTag(TestTags.ShowPasswordIcon)) {
Icon(painter = painterResource(icon), contentDescription = stringResource(contentDescription))
}
}
diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/PasswordReset.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/PasswordReset.kt
index 74168b50..1de1da1c 100644
--- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/PasswordReset.kt
+++ b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/PasswordReset.kt
@@ -53,7 +53,7 @@ fun PasswordReset(
state = state.form
)
AuthenticatorButton(
- modifier = Modifier.testTag("PasswordResetButton"),
+ modifier = Modifier.testTag(TestTags.PasswordResetButton),
onClick = { scope.launch { state.submitPasswordReset() } },
loading = !state.form.enabled
)
diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignIn.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignIn.kt
index dc2c98da..35f1ade0 100644
--- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignIn.kt
+++ b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignIn.kt
@@ -54,7 +54,7 @@ fun SignIn(
onClick = { scope.launch { state.signIn() } },
loading = !state.form.enabled,
label = stringResource(R.string.amplify_ui_authenticator_button_signin),
- modifier = Modifier.testTag("SignInButton")
+ modifier = Modifier.testTag(TestTags.SignInButton)
)
footerContent(state)
}
diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignUp.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignUp.kt
index 22e60fd3..d6aeb0a2 100644
--- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignUp.kt
+++ b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/SignUp.kt
@@ -52,7 +52,7 @@ fun SignUp(
onClick = { scope.launch { state.signUp() } },
loading = !state.form.enabled,
label = stringResource(R.string.amplify_ui_authenticator_button_signup),
- modifier = Modifier.testTag("SignUpButton")
+ modifier = Modifier.testTag(TestTags.SignUpButton)
)
footerContent(state)
}
diff --git a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/TestTags.kt b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/TestTags.kt
index 6af214c3..208195ae 100644
--- a/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/TestTags.kt
+++ b/authenticator/src/main/java/com/amplifyframework/ui/authenticator/ui/TestTags.kt
@@ -19,4 +19,10 @@ internal object TestTags {
const val SignInConfirmButton = "SignInConfirmButton"
const val BackToSignInButton = "BackToSignInButton"
const val CopyKeyButton = "CopyKeyButton"
+ const val SignInButton = "SignInButton"
+ const val SignUpButton = "SignUpButton"
+ const val PasswordResetButton = "PasswordResetButton"
+ const val AuthenticatorTitle = "AuthenticatorTitle"
+
+ const val ShowPasswordIcon = "ShowPasswordIcon"
}
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ScreenshotTestBase.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/AuthenticatorUiTest.kt
similarity index 55%
rename from authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ScreenshotTestBase.kt
rename to authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/AuthenticatorUiTest.kt
index bc673736..670dda89 100644
--- a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/ScreenshotTestBase.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/AuthenticatorUiTest.kt
@@ -1,5 +1,5 @@
/*
- * Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
* Licensed under the Apache License, Version 2.0 (the "License").
* You may not use this file except in compliance with the License.
@@ -13,35 +13,24 @@
* permissions and limitations under the License.
*/
-package com.amplifyframework.ui.authenticator
+package com.amplifyframework.ui.authenticator.testUtil
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.padding
-import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
-import app.cash.paparazzi.DeviceConfig
-import app.cash.paparazzi.Paparazzi
import com.amplifyframework.ui.authenticator.theme.AmplifyTheme
-import org.junit.Rule
+import com.amplifyframework.ui.testing.ComposeTest
-abstract class ScreenshotTestBase {
-
- @get:Rule
- val screenshotRule = Paparazzi(
- deviceConfig = DeviceConfig.PIXEL_6,
- showSystemUi = false
- )
-
- protected open fun screenshot(name: String? = null, content: @Composable () -> Unit) =
- screenshotRule.snapshot(name) {
+abstract class AuthenticatorUiTest : ComposeTest() {
+ override fun setContent(content: @Composable () -> Unit) {
+ super.setContent {
AmplifyTheme {
- Surface {
- Box(modifier = Modifier.padding(top = 56.dp)) {
- content()
- }
+ Box(modifier = Modifier.padding(top = 16.dp)) {
+ content()
}
}
}
+ }
}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/MockStates.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/MockStates.kt
new file mode 100644
index 00000000..e980edd2
--- /dev/null
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/MockStates.kt
@@ -0,0 +1,111 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.amplifyframework.ui.authenticator.testUtil
+
+import com.amplifyframework.auth.AuthCodeDeliveryDetails
+import com.amplifyframework.auth.AuthUserAttributeKey
+import com.amplifyframework.auth.MFAType
+import com.amplifyframework.ui.authenticator.auth.PasswordCriteria
+import com.amplifyframework.ui.authenticator.auth.SignInMethod
+import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
+import com.amplifyframework.ui.authenticator.forms.FormData
+import com.amplifyframework.ui.authenticator.states.PasswordResetStateImpl
+import com.amplifyframework.ui.authenticator.states.SignInConfirmMfaStateImpl
+import com.amplifyframework.ui.authenticator.states.SignInConfirmTotpCodeStateImpl
+import com.amplifyframework.ui.authenticator.states.SignInContinueWithEmailSetupStateImpl
+import com.amplifyframework.ui.authenticator.states.SignInContinueWithMfaSelectionStateImpl
+import com.amplifyframework.ui.authenticator.states.SignInContinueWithMfaSetupSelectionStateImpl
+import com.amplifyframework.ui.authenticator.states.SignInContinueWithTotpSetupStateImpl
+import com.amplifyframework.ui.authenticator.states.SignInStateImpl
+import com.amplifyframework.ui.authenticator.states.SignUpStateImpl
+
+internal fun mockSignInState() = SignInStateImpl(
+ signInMethod = SignInMethod.Username,
+ onSubmit = { _, _ -> },
+ onMoveTo = { }
+)
+
+internal fun mockSignUpState() = SignUpStateImpl(
+ signInMethod = SignInMethod.Username,
+ signUpAttributes = listOf(AuthUserAttributeKey.email()),
+ passwordCriteria = PasswordCriteria(8, false, false, false, false),
+ signUpForm = FormData(emptyList()),
+ onSubmit = { _, _, _ -> },
+ onMoveTo = { }
+)
+
+internal fun mockPasswordResetState() = PasswordResetStateImpl(
+ signInMethod = SignInMethod.Username,
+ onSubmit = {},
+ onMoveTo = {}
+)
+
+internal fun mockSignInConfirmTotpCodeState(
+ onSubmit: (String) -> Unit = { },
+ onMoveTo: (AuthenticatorInitialStep) -> Unit = { }
+) = SignInConfirmTotpCodeStateImpl(
+ onSubmit = onSubmit,
+ onMoveTo = onMoveTo
+)
+
+internal fun mockSignInContinueWithEmailSetupState(
+ onSubmit: suspend (email: String) -> Unit = {},
+ onMoveTo: (step: AuthenticatorInitialStep) -> Unit = {}
+) = SignInContinueWithEmailSetupStateImpl(
+ onSubmit = onSubmit,
+ onMoveTo = onMoveTo
+)
+
+internal fun mockSignInContinueWithMfaSelectionState(
+ allowedMfaTypes: Set = MFAType.entries.toSet(),
+ onSubmit: (String) -> Unit = {},
+ onMoveTo: (AuthenticatorInitialStep) -> Unit = {}
+) = SignInContinueWithMfaSelectionStateImpl(
+ allowedMfaTypes = allowedMfaTypes,
+ onSubmit = onSubmit,
+ onMoveTo = onMoveTo
+)
+
+internal fun mockSignInContinueWithTotpSetupState(
+ sharedSecret: String = "",
+ setupUri: String = "",
+ onSubmit: (String) -> Unit = { },
+ onMoveTo: (AuthenticatorInitialStep) -> Unit = { }
+) = SignInContinueWithTotpSetupStateImpl(
+ sharedSecret = sharedSecret,
+ setupUri = setupUri,
+ onSubmit = onSubmit,
+ onMoveTo = onMoveTo
+)
+
+internal fun mockSignInConfirmMfaState(
+ deliveryDetails: AuthCodeDeliveryDetails = AuthCodeDeliveryDetails(
+ "123-123-1234",
+ AuthCodeDeliveryDetails.DeliveryMedium.SMS
+ )
+) = SignInConfirmMfaStateImpl(
+ deliveryDetails = deliveryDetails,
+ onSubmit = { },
+ onMoveTo = { }
+)
+
+internal fun mockSignInContinueWithMfaSetupSelectionState(
+ allowedMfaTypes: Set = setOf(MFAType.TOTP, MFAType.SMS, MFAType.EMAIL)
+) = SignInContinueWithMfaSetupSelectionStateImpl(
+ allowedMfaTypes = allowedMfaTypes,
+ onSubmit = { },
+ onMoveTo = { }
+)
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/theme/Color.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/theme/Color.kt
similarity index 100%
rename from authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/theme/Color.kt
rename to authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/theme/Color.kt
diff --git a/authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/theme/Theme.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/theme/Theme.kt
similarity index 100%
rename from authenticator-screenshots/src/test/java/com/amplifyframework/ui/authenticator/theme/Theme.kt
rename to authenticator/src/test/java/com/amplifyframework/ui/authenticator/testUtil/theme/Theme.kt
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/PasswordResetTest.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/PasswordResetTest.kt
index 651da8f8..406e6681 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/PasswordResetTest.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/PasswordResetTest.kt
@@ -15,13 +15,16 @@
package com.amplifyframework.ui.authenticator.ui
-import com.amplifyframework.ui.authenticator.auth.SignInMethod
-import com.amplifyframework.ui.authenticator.states.PasswordResetStateImpl
+import com.amplifyframework.ui.authenticator.forms.FieldError
+import com.amplifyframework.ui.authenticator.forms.FieldKey
+import com.amplifyframework.ui.authenticator.forms.setFieldError
+import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
+import com.amplifyframework.ui.authenticator.testUtil.mockPasswordResetState
import com.amplifyframework.ui.authenticator.ui.robots.passwordReset
-import com.amplifyframework.ui.testing.ComposeTest
+import com.amplifyframework.ui.testing.ScreenshotTest
import org.junit.Test
-class PasswordResetTest : ComposeTest() {
+class PasswordResetTest : AuthenticatorUiTest() {
@Test
fun `title is reset password`() {
@@ -53,9 +56,24 @@ class PasswordResetTest : ComposeTest() {
}
}
- private fun mockPasswordResetState() = PasswordResetStateImpl(
- signInMethod = SignInMethod.Username,
- onSubmit = {},
- onMoveTo = {}
- )
+ @Test
+ @ScreenshotTest
+ fun `default state`() {
+ setContent {
+ PasswordReset(state = mockPasswordResetState())
+ }
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `username not found`() {
+ val state = mockPasswordResetState()
+ setContent {
+ PasswordReset(state = state)
+ }
+ passwordReset {
+ setUsername("username")
+ }
+ state.form.setFieldError(FieldKey.Username, FieldError.NotFound)
+ }
}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmMfaTest.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmMfaTest.kt
new file mode 100644
index 00000000..137b89bc
--- /dev/null
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmMfaTest.kt
@@ -0,0 +1,52 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.amplifyframework.ui.authenticator.ui
+
+import com.amplifyframework.ui.authenticator.forms.FieldError
+import com.amplifyframework.ui.authenticator.forms.FieldKey
+import com.amplifyframework.ui.authenticator.forms.setFieldError
+import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
+import com.amplifyframework.ui.authenticator.testUtil.mockSignInConfirmMfaState
+import com.amplifyframework.ui.authenticator.ui.robots.signInConfirmMfa
+import com.amplifyframework.ui.testing.ScreenshotTest
+import org.junit.Test
+
+class SignInConfirmMfaTest : AuthenticatorUiTest() {
+ @Test
+ @ScreenshotTest
+ fun `default state`() {
+ setContent {
+ SignInConfirmMfa(
+ mockSignInConfirmMfaState()
+ )
+ }
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `incorrect code`() {
+ val state = mockSignInConfirmMfaState()
+ setContent {
+ SignInConfirmMfa(state)
+ }
+
+ signInConfirmMfa {
+ setConfirmationCode("123456")
+ }
+
+ state.form.setFieldError(FieldKey.ConfirmationCode, FieldError.ConfirmationCodeIncorrect)
+ }
+}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmTotpCodeTest.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmTotpCodeTest.kt
index 853d3552..5645d3d7 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmTotpCodeTest.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInConfirmTotpCodeTest.kt
@@ -17,14 +17,18 @@ package com.amplifyframework.ui.authenticator.ui
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.states.SignInConfirmTotpCodeStateImpl
+import com.amplifyframework.ui.authenticator.forms.FieldError
+import com.amplifyframework.ui.authenticator.forms.FieldKey
+import com.amplifyframework.ui.authenticator.forms.setFieldError
+import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
+import com.amplifyframework.ui.authenticator.testUtil.mockSignInConfirmTotpCodeState
import com.amplifyframework.ui.authenticator.ui.robots.signInConfirmTotpCode
-import com.amplifyframework.ui.testing.ComposeTest
+import com.amplifyframework.ui.testing.ScreenshotTest
import io.mockk.mockk
import io.mockk.verify
import org.junit.Test
-class SignInConfirmTotpCodeTest : ComposeTest() {
+class SignInConfirmTotpCodeTest : AuthenticatorUiTest() {
@Test
fun `title is Enter your one-time passcode`() {
setContent {
@@ -76,11 +80,24 @@ class SignInConfirmTotpCodeTest : ComposeTest() {
}
}
- private fun mockSignInConfirmTotpCodeState(
- onSubmit: (String) -> Unit = { },
- onMoveTo: (AuthenticatorInitialStep) -> Unit = { }
- ) = SignInConfirmTotpCodeStateImpl(
- onSubmit = onSubmit,
- onMoveTo = onMoveTo
- )
+ @Test
+ @ScreenshotTest
+ fun `default state`() {
+ setContent {
+ SignInConfirmTotpCode(state = mockSignInConfirmTotpCodeState())
+ }
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `invalid code`() {
+ val state = mockSignInConfirmTotpCodeState()
+ setContent {
+ SignInConfirmTotpCode(state = state)
+ }
+ signInConfirmTotpCode {
+ setConfirmationCode("123456")
+ }
+ state.form.setFieldError(FieldKey.ConfirmationCode, FieldError.ConfirmationCodeIncorrect)
+ }
}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithEmailSetupTest.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithEmailSetupTest.kt
index 29b025b1..def821a4 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithEmailSetupTest.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithEmailSetupTest.kt
@@ -17,14 +17,15 @@ package com.amplifyframework.ui.authenticator.ui
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.states.SignInContinueWithEmailSetupStateImpl
+import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
+import com.amplifyframework.ui.authenticator.testUtil.mockSignInContinueWithEmailSetupState
import com.amplifyframework.ui.authenticator.ui.robots.signInContinueWithEmailSetup
-import com.amplifyframework.ui.testing.ComposeTest
+import com.amplifyframework.ui.testing.ScreenshotTest
import io.mockk.mockk
import io.mockk.verify
import org.junit.Test
-class SignInContinueWithEmailSetupTest : ComposeTest() {
+class SignInContinueWithEmailSetupTest : AuthenticatorUiTest() {
@Test
fun `title is Setup Two-Factor Auth Method`() {
@@ -81,11 +82,11 @@ class SignInContinueWithEmailSetupTest : ComposeTest() {
}
}
- private fun mockSignInContinueWithEmailSetupState(
- onSubmit: suspend (email: String) -> Unit = {},
- onMoveTo: (step: AuthenticatorInitialStep) -> Unit = {}
- ) = SignInContinueWithEmailSetupStateImpl(
- onSubmit = onSubmit,
- onMoveTo = onMoveTo
- )
+ @Test
+ @ScreenshotTest
+ fun `default state`() {
+ setContent {
+ SignInContinueWithEmailSetup(mockSignInContinueWithEmailSetupState())
+ }
+ }
}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionTest.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionTest.kt
index 9cd7e382..8c804241 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionTest.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSelectionTest.kt
@@ -4,14 +4,15 @@ import com.amplifyframework.auth.MFAType
import com.amplifyframework.auth.cognito.challengeResponse
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.states.SignInContinueWithMfaSelectionStateImpl
+import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
+import com.amplifyframework.ui.authenticator.testUtil.mockSignInContinueWithMfaSelectionState
import com.amplifyframework.ui.authenticator.ui.robots.signInContinueWithMfaSelection
-import com.amplifyframework.ui.testing.ComposeTest
+import com.amplifyframework.ui.testing.ScreenshotTest
import io.mockk.mockk
import io.mockk.verify
import org.junit.Test
-class SignInContinueWithMfaSelectionTest : ComposeTest() {
+class SignInContinueWithMfaSelectionTest : AuthenticatorUiTest() {
@Test
fun `title is Choose your two-factor authentication method`() {
@@ -106,13 +107,11 @@ class SignInContinueWithMfaSelectionTest : ComposeTest() {
}
}
- private fun mockSignInContinueWithMfaSelectionState(
- allowedMfaTypes: Set = MFAType.values().toSet(),
- onSubmit: (String) -> Unit = {},
- onMoveTo: (AuthenticatorInitialStep) -> Unit = {}
- ) = SignInContinueWithMfaSelectionStateImpl(
- allowedMfaTypes = allowedMfaTypes,
- onSubmit = onSubmit,
- onMoveTo = onMoveTo
- )
+ @Test
+ @ScreenshotTest
+ fun `default state`() {
+ setContent {
+ SignInContinueWithMfaSelection(mockSignInContinueWithMfaSelectionState())
+ }
+ }
}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSetupSelectionTest.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSetupSelectionTest.kt
new file mode 100644
index 00000000..53e99bab
--- /dev/null
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithMfaSetupSelectionTest.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.amplifyframework.ui.authenticator.ui
+
+import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
+import com.amplifyframework.ui.authenticator.testUtil.mockSignInContinueWithMfaSetupSelectionState
+import org.junit.Test
+
+class SignInContinueWithMfaSetupSelectionTest : AuthenticatorUiTest() {
+ @Test
+ fun `default state`() {
+ setContent {
+ SignInContinueWithMfaSetupSelection(mockSignInContinueWithMfaSetupSelectionState())
+ }
+ }
+}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithTotpCodeTest.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithTotpSetupTest.kt
similarity index 84%
rename from authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithTotpCodeTest.kt
rename to authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithTotpSetupTest.kt
index 36a9d0f3..b4fc59ab 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithTotpCodeTest.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInContinueWithTotpSetupTest.kt
@@ -19,16 +19,17 @@ import android.content.ClipboardManager
import android.content.Context
import com.amplifyframework.ui.authenticator.enums.AuthenticatorInitialStep
import com.amplifyframework.ui.authenticator.enums.AuthenticatorStep
-import com.amplifyframework.ui.authenticator.states.SignInContinueWithTotpSetupStateImpl
+import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
+import com.amplifyframework.ui.authenticator.testUtil.mockSignInContinueWithTotpSetupState
import com.amplifyframework.ui.authenticator.ui.robots.signInContinueWithTotpSetup
-import com.amplifyframework.ui.testing.ComposeTest
+import com.amplifyframework.ui.testing.ScreenshotTest
import io.kotest.matchers.shouldBe
import io.mockk.mockk
import io.mockk.verify
import org.junit.Test
import org.robolectric.RuntimeEnvironment
-class SignInContinueWithTotpCodeTest : ComposeTest() {
+class SignInContinueWithTotpSetupTest : AuthenticatorUiTest() {
@Test
fun `title is Enable Two-Factor Auth`() {
setContent {
@@ -92,17 +93,13 @@ class SignInContinueWithTotpCodeTest : ComposeTest() {
getClipboardContent() shouldBe "secret!"
}
- private fun mockSignInContinueWithTotpSetupState(
- sharedSecret: String = "",
- setupUri: String = "",
- onSubmit: (String) -> Unit = { },
- onMoveTo: (AuthenticatorInitialStep) -> Unit = { }
- ) = SignInContinueWithTotpSetupStateImpl(
- sharedSecret = sharedSecret,
- setupUri = setupUri,
- onSubmit = onSubmit,
- onMoveTo = onMoveTo
- )
+ @Test
+ @ScreenshotTest
+ fun `default state`() {
+ setContent {
+ SignInContinueWithTotpSetup(state = mockSignInContinueWithTotpSetupState())
+ }
+ }
private fun getClipboardContent(): String? {
val clipboardManager =
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInTest.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInTest.kt
index a588be8e..612d0140 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInTest.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignInTest.kt
@@ -15,13 +15,16 @@
package com.amplifyframework.ui.authenticator.ui
-import com.amplifyframework.ui.authenticator.auth.SignInMethod
-import com.amplifyframework.ui.authenticator.states.SignInStateImpl
+import com.amplifyframework.ui.authenticator.forms.FieldError
+import com.amplifyframework.ui.authenticator.forms.FieldKey
+import com.amplifyframework.ui.authenticator.forms.setFieldError
+import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
+import com.amplifyframework.ui.authenticator.testUtil.mockSignInState
import com.amplifyframework.ui.authenticator.ui.robots.signIn
-import com.amplifyframework.ui.testing.ComposeTest
+import com.amplifyframework.ui.testing.ScreenshotTest
import org.junit.Test
-class SignInTest : ComposeTest() {
+class SignInTest : AuthenticatorUiTest() {
@Test
fun `title is Sign In`() {
@@ -43,9 +46,49 @@ class SignInTest : ComposeTest() {
}
}
- private fun mockSignInState() = SignInStateImpl(
- signInMethod = SignInMethod.Username,
- onSubmit = { _, _ -> },
- onMoveTo = { }
- )
+ @Test
+ @ScreenshotTest
+ fun `default state`() {
+ setContent {
+ SignIn(state = mockSignInState())
+ }
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `ready to submit`() {
+ setContent {
+ SignIn(state = mockSignInState())
+ }
+ signIn {
+ setUsername("username")
+ setPassword("password")
+ }
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `password visible`() {
+ setContent {
+ SignIn(state = mockSignInState())
+ }
+ signIn {
+ setUsername("username")
+ setPassword("password")
+ clickShowPassword()
+ }
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `username not found`() {
+ val state = mockSignInState()
+ setContent {
+ SignIn(state = state)
+ }
+ signIn {
+ setUsername("username")
+ }
+ state.form.setFieldError(FieldKey.Username, FieldError.NotFound)
+ }
}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignUpTest.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignUpTest.kt
index 1c9c8b49..e9fe1156 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignUpTest.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/SignUpTest.kt
@@ -15,15 +15,17 @@
package com.amplifyframework.ui.authenticator.ui
-import com.amplifyframework.ui.authenticator.auth.PasswordCriteria
-import com.amplifyframework.ui.authenticator.auth.SignInMethod
-import com.amplifyframework.ui.authenticator.forms.FormData
-import com.amplifyframework.ui.authenticator.states.SignUpStateImpl
+import com.amplifyframework.ui.authenticator.forms.FieldError
+import com.amplifyframework.ui.authenticator.forms.FieldKey
+import com.amplifyframework.ui.authenticator.forms.PasswordError
+import com.amplifyframework.ui.authenticator.forms.setFieldError
+import com.amplifyframework.ui.authenticator.testUtil.AuthenticatorUiTest
+import com.amplifyframework.ui.authenticator.testUtil.mockSignUpState
import com.amplifyframework.ui.authenticator.ui.robots.signUp
-import com.amplifyframework.ui.testing.ComposeTest
+import com.amplifyframework.ui.testing.ScreenshotTest
import org.junit.Test
-class SignUpTest : ComposeTest() {
+class SignUpTest : AuthenticatorUiTest() {
@Test
fun `title is Create Account`() {
@@ -45,12 +47,117 @@ class SignUpTest : ComposeTest() {
}
}
- private fun mockSignUpState() = SignUpStateImpl(
- signInMethod = SignInMethod.Username,
- signUpAttributes = emptyList(),
- passwordCriteria = PasswordCriteria(8, false, false, false, false),
- signUpForm = FormData(emptyList()),
- onSubmit = { _, _, _ -> },
- onMoveTo = { }
- )
+ @Test
+ @ScreenshotTest
+ fun `default state`() {
+ setContent {
+ SignUp(state = mockSignUpState())
+ }
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `ready to submit`() {
+ setContent {
+ SignUp(state = mockSignUpState())
+ }
+ signUp {
+ setUsername("username")
+ setPassword("password")
+ setConfirmPassword("password")
+ setEmail("email@email.com")
+ }
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `password visible`() {
+ setContent {
+ SignUp(state = mockSignUpState())
+ }
+ signUp {
+ setUsername("username")
+ setPassword("password")
+ setConfirmPassword("password")
+ setEmail("email@email.com")
+
+ clickShowPassword(FieldKey.Password)
+ clickShowPassword(FieldKey.ConfirmPassword)
+ }
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `username exists`() {
+ val state = mockSignUpState()
+ setContent {
+ SignUp(state = state)
+ }
+ signUp {
+ setUsername("username")
+ setPassword("password")
+ setConfirmPassword("password")
+ setEmail("email@email.com")
+ }
+
+ state.form.setFieldError(FieldKey.Username, FieldError.FieldValueExists)
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `invalid password`() {
+ val state = mockSignUpState()
+ setContent {
+ SignUp(state = state)
+ }
+ signUp {
+ setUsername("username")
+ setPassword("password")
+ setConfirmPassword("password")
+ setEmail("email@email.com")
+ }
+
+ val error = FieldError.InvalidPassword(
+ listOf(
+ PasswordError.InvalidPasswordLength(10),
+ PasswordError.InvalidPasswordMissingUpper,
+ PasswordError.InvalidPasswordMissingSpecial,
+ PasswordError.InvalidPasswordMissingNumber
+ )
+ )
+
+ state.form.setFieldError(FieldKey.Password, error)
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `passwords do not match`() {
+ val state = mockSignUpState()
+ setContent {
+ SignUp(state = state)
+ }
+ signUp {
+ setUsername("username")
+ setPassword("password")
+ setConfirmPassword("password")
+ setEmail("email@email.com")
+ }
+ state.form.setFieldError(FieldKey.ConfirmPassword, FieldError.PasswordsDoNotMatch)
+ }
+
+ @Test
+ @ScreenshotTest
+ fun `invalid email`() {
+ val state = mockSignUpState()
+ setContent {
+ SignUp(state = state)
+ }
+ signUp {
+ setUsername("username")
+ setPassword("password")
+ setConfirmPassword("password")
+ setEmail("email@email.com")
+ }
+ state.form.setFieldError(FieldKey.Email, FieldError.InvalidFormat)
+ }
}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/PasswordResetRobot.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/PasswordResetRobot.kt
index 9819518f..48330d36 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/PasswordResetRobot.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/PasswordResetRobot.kt
@@ -16,11 +16,14 @@
package com.amplifyframework.ui.authenticator.ui.robots
import androidx.compose.ui.test.junit4.ComposeTestRule
+import com.amplifyframework.ui.authenticator.forms.FieldKey
+import com.amplifyframework.ui.authenticator.ui.TestTags
import com.amplifyframework.ui.testing.ComposeTest
fun ComposeTest.passwordReset(func: PasswordResetRobot.() -> Unit) = PasswordResetRobot(composeTestRule).func()
class PasswordResetRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
+ fun hasSubmitButton(expected: String) = assertExists(TestTags.PasswordResetButton, expected)
- fun hasSubmitButton(expected: String) = assertExists("PasswordResetButton", expected)
+ fun setUsername(value: String) = setFieldContent(FieldKey.Username, value)
}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/ScreenLevelRobot.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/ScreenLevelRobot.kt
index 8c32d706..db48f964 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/ScreenLevelRobot.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/ScreenLevelRobot.kt
@@ -15,13 +15,21 @@
package com.amplifyframework.ui.authenticator.ui.robots
+import androidx.compose.ui.test.hasAnyAncestor
+import androidx.compose.ui.test.hasTestTag
import androidx.compose.ui.test.junit4.ComposeTestRule
+import androidx.compose.ui.test.performClick
import com.amplifyframework.ui.authenticator.forms.FieldKey
+import com.amplifyframework.ui.authenticator.ui.TestTags
import com.amplifyframework.ui.testing.ComposeRobot
abstract class ScreenLevelRobot(rule: ComposeTestRule) : ComposeRobot(rule) {
// Check that the composable has the expected title
- fun hasTitle(expected: String) = assertExists("AuthenticatorTitle", expected)
+ fun hasTitle(expected: String) = assertExists(TestTags.AuthenticatorTitle, expected)
fun setFieldContent(key: FieldKey, content: String) = writeTo(key.toString(), content)
+
+ fun clickOnShowIcon(key: FieldKey) = composeTestRule.onNode(
+ hasTestTag(TestTags.ShowPasswordIcon) and hasAnyAncestor(hasTestTag(key.toString()))
+ ).performClick()
}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInConfirmMfaRobot.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInConfirmMfaRobot.kt
new file mode 100644
index 00000000..a9978757
--- /dev/null
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInConfirmMfaRobot.kt
@@ -0,0 +1,29 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.amplifyframework.ui.authenticator.ui.robots
+
+import androidx.compose.ui.test.junit4.ComposeTestRule
+import com.amplifyframework.ui.authenticator.forms.FieldKey
+import com.amplifyframework.ui.testing.ComposeTest
+
+fun ComposeTest.signInConfirmMfa(func: SignInConfirmMfaRobot.() -> Unit) = SignInConfirmMfaRobot(
+ composeTestRule
+).func()
+
+class SignInConfirmMfaRobot(composeTestRule: ComposeTestRule) : ScreenLevelRobot(composeTestRule) {
+
+ fun setConfirmationCode(value: String) = setFieldContent(FieldKey.ConfirmationCode, value)
+}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInRobot.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInRobot.kt
index 4a7daef9..0555bad3 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInRobot.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignInRobot.kt
@@ -16,10 +16,15 @@
package com.amplifyframework.ui.authenticator.ui.robots
import androidx.compose.ui.test.junit4.ComposeTestRule
+import com.amplifyframework.ui.authenticator.forms.FieldKey
import com.amplifyframework.ui.testing.ComposeTest
fun ComposeTest.signIn(func: SignInRobot.() -> Unit) = SignInRobot(composeTestRule).func()
class SignInRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
fun hasSubmitButton(expected: String) = assertExists("SignInButton", expected)
+
+ fun setUsername(value: String) = setFieldContent(FieldKey.Username, value)
+ fun setPassword(value: String) = setFieldContent(FieldKey.Password, value)
+ fun clickShowPassword() = clickOnShowIcon(FieldKey.Password)
}
diff --git a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignUpRobot.kt b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignUpRobot.kt
index 5318f39c..d7774645 100644
--- a/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignUpRobot.kt
+++ b/authenticator/src/test/java/com/amplifyframework/ui/authenticator/ui/robots/SignUpRobot.kt
@@ -16,10 +16,18 @@
package com.amplifyframework.ui.authenticator.ui.robots
import androidx.compose.ui.test.junit4.ComposeTestRule
+import com.amplifyframework.ui.authenticator.forms.FieldKey
+import com.amplifyframework.ui.authenticator.ui.TestTags
import com.amplifyframework.ui.testing.ComposeTest
fun ComposeTest.signUp(func: SignUpRobot.() -> Unit) = SignUpRobot(composeTestRule).func()
class SignUpRobot(rule: ComposeTestRule) : ScreenLevelRobot(rule) {
- fun hasSubmitButton(expected: String) = assertExists("SignUpButton", expected)
+ fun hasSubmitButton(expected: String) = assertExists(TestTags.SignUpButton, expected)
+
+ fun setUsername(value: String) = setFieldContent(FieldKey.Username, value)
+ fun setPassword(value: String) = setFieldContent(FieldKey.Password, value)
+ fun setConfirmPassword(value: String) = setFieldContent(FieldKey.ConfirmPassword, value)
+ fun setEmail(value: String) = setFieldContent(FieldKey.Email, value)
+ fun clickShowPassword(fieldKey: FieldKey) = clickOnShowIcon(fieldKey)
}
diff --git a/authenticator/src/test/screenshots/PasswordResetTest_default-state.png b/authenticator/src/test/screenshots/PasswordResetTest_default-state.png
new file mode 100644
index 00000000..99f3aea3
Binary files /dev/null and b/authenticator/src/test/screenshots/PasswordResetTest_default-state.png differ
diff --git a/authenticator/src/test/screenshots/PasswordResetTest_username-not-found.png b/authenticator/src/test/screenshots/PasswordResetTest_username-not-found.png
new file mode 100644
index 00000000..865a92b8
Binary files /dev/null and b/authenticator/src/test/screenshots/PasswordResetTest_username-not-found.png differ
diff --git a/authenticator/src/test/screenshots/SignInConfirmMfaTest_default-state.png b/authenticator/src/test/screenshots/SignInConfirmMfaTest_default-state.png
new file mode 100644
index 00000000..ef5a1f93
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInConfirmMfaTest_default-state.png differ
diff --git a/authenticator/src/test/screenshots/SignInConfirmMfaTest_incorrect-code.png b/authenticator/src/test/screenshots/SignInConfirmMfaTest_incorrect-code.png
new file mode 100644
index 00000000..94165b5c
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInConfirmMfaTest_incorrect-code.png differ
diff --git a/authenticator/src/test/screenshots/SignInConfirmTotpCodeTest_default-state.png b/authenticator/src/test/screenshots/SignInConfirmTotpCodeTest_default-state.png
new file mode 100644
index 00000000..c72ff482
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInConfirmTotpCodeTest_default-state.png differ
diff --git a/authenticator/src/test/screenshots/SignInConfirmTotpCodeTest_invalid-code.png b/authenticator/src/test/screenshots/SignInConfirmTotpCodeTest_invalid-code.png
new file mode 100644
index 00000000..5e903b17
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInConfirmTotpCodeTest_invalid-code.png differ
diff --git a/authenticator/src/test/screenshots/SignInContinueWithEmailSetupTest_default-state.png b/authenticator/src/test/screenshots/SignInContinueWithEmailSetupTest_default-state.png
new file mode 100644
index 00000000..f3f66e19
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInContinueWithEmailSetupTest_default-state.png differ
diff --git a/authenticator/src/test/screenshots/SignInContinueWithMfaSelectionTest_default-state.png b/authenticator/src/test/screenshots/SignInContinueWithMfaSelectionTest_default-state.png
new file mode 100644
index 00000000..a5f5602d
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInContinueWithMfaSelectionTest_default-state.png differ
diff --git a/authenticator/src/test/screenshots/SignInContinueWithTotpSetupTest_default-state.png b/authenticator/src/test/screenshots/SignInContinueWithTotpSetupTest_default-state.png
new file mode 100644
index 00000000..1081a32f
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInContinueWithTotpSetupTest_default-state.png differ
diff --git a/authenticator/src/test/screenshots/SignInTest_default-state.png b/authenticator/src/test/screenshots/SignInTest_default-state.png
new file mode 100644
index 00000000..9f4ccc9a
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInTest_default-state.png differ
diff --git a/authenticator/src/test/screenshots/SignInTest_password-visible.png b/authenticator/src/test/screenshots/SignInTest_password-visible.png
new file mode 100644
index 00000000..fa61261d
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInTest_password-visible.png differ
diff --git a/authenticator/src/test/screenshots/SignInTest_ready-to-submit.png b/authenticator/src/test/screenshots/SignInTest_ready-to-submit.png
new file mode 100644
index 00000000..9df29da5
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInTest_ready-to-submit.png differ
diff --git a/authenticator/src/test/screenshots/SignInTest_username-not-found.png b/authenticator/src/test/screenshots/SignInTest_username-not-found.png
new file mode 100644
index 00000000..b192482f
Binary files /dev/null and b/authenticator/src/test/screenshots/SignInTest_username-not-found.png differ
diff --git a/authenticator/src/test/screenshots/SignUpTest_default-state.png b/authenticator/src/test/screenshots/SignUpTest_default-state.png
new file mode 100644
index 00000000..c4419737
Binary files /dev/null and b/authenticator/src/test/screenshots/SignUpTest_default-state.png differ
diff --git a/authenticator/src/test/screenshots/SignUpTest_invalid-email.png b/authenticator/src/test/screenshots/SignUpTest_invalid-email.png
new file mode 100644
index 00000000..baf6ffbc
Binary files /dev/null and b/authenticator/src/test/screenshots/SignUpTest_invalid-email.png differ
diff --git a/authenticator/src/test/screenshots/SignUpTest_invalid-password.png b/authenticator/src/test/screenshots/SignUpTest_invalid-password.png
new file mode 100644
index 00000000..8b6364b6
Binary files /dev/null and b/authenticator/src/test/screenshots/SignUpTest_invalid-password.png differ
diff --git a/authenticator/src/test/screenshots/SignUpTest_password-visible.png b/authenticator/src/test/screenshots/SignUpTest_password-visible.png
new file mode 100644
index 00000000..35f465de
Binary files /dev/null and b/authenticator/src/test/screenshots/SignUpTest_password-visible.png differ
diff --git a/authenticator/src/test/screenshots/SignUpTest_passwords-do-not-match.png b/authenticator/src/test/screenshots/SignUpTest_passwords-do-not-match.png
new file mode 100644
index 00000000..d2c08a28
Binary files /dev/null and b/authenticator/src/test/screenshots/SignUpTest_passwords-do-not-match.png differ
diff --git a/authenticator/src/test/screenshots/SignUpTest_ready-to-submit.png b/authenticator/src/test/screenshots/SignUpTest_ready-to-submit.png
new file mode 100644
index 00000000..ad1bc4ec
Binary files /dev/null and b/authenticator/src/test/screenshots/SignUpTest_ready-to-submit.png differ
diff --git a/authenticator/src/test/screenshots/SignUpTest_username-exists.png b/authenticator/src/test/screenshots/SignUpTest_username-exists.png
new file mode 100644
index 00000000..9f6fbcec
Binary files /dev/null and b/authenticator/src/test/screenshots/SignUpTest_username-exists.png differ
diff --git a/build-logic/plugins/build.gradle.kts b/build-logic/plugins/build.gradle.kts
index 08e13de2..993f79f1 100644
--- a/build-logic/plugins/build.gradle.kts
+++ b/build-logic/plugins/build.gradle.kts
@@ -1,3 +1,5 @@
+import org.jetbrains.kotlin.ir.backend.js.compile
+
/*
* Copyright 2023 Amazon.com, Inc. or its affiliates. All Rights Reserved.
*
@@ -38,6 +40,7 @@ dependencies {
compileOnly(libs.plugin.kover)
compileOnly(libs.plugin.ktlint)
compileOnly(libs.plugin.licensee)
+ compileOnly(libs.plugin.roborazzi)
}
gradlePlugin {
@@ -70,5 +73,9 @@ gradlePlugin {
id = "amplify.android.publishing"
implementationClass = "PublishingConventionPlugin"
}
+ register("screenshots") {
+ id = "amplify.android.screenshots"
+ implementationClass = "ScreenshotConventionPlugin"
+ }
}
}
diff --git a/build-logic/plugins/src/main/kotlin/ComponentConventionPlugin.kt b/build-logic/plugins/src/main/kotlin/ComponentConventionPlugin.kt
index 69218eae..72c0173c 100644
--- a/build-logic/plugins/src/main/kotlin/ComponentConventionPlugin.kt
+++ b/build-logic/plugins/src/main/kotlin/ComponentConventionPlugin.kt
@@ -34,6 +34,7 @@ class ComponentConventionPlugin : Plugin {
pluginManager.apply("amplify.android.kover")
pluginManager.apply("amplify.android.api.validator")
pluginManager.apply("amplify.android.licenses")
+ pluginManager.apply("amplify.android.screenshots")
tasks.withType().configureEach {
kotlinOptions {
diff --git a/build-logic/plugins/src/main/kotlin/ScreenshotConventionPlugin.kt b/build-logic/plugins/src/main/kotlin/ScreenshotConventionPlugin.kt
new file mode 100644
index 00000000..66ed48f6
--- /dev/null
+++ b/build-logic/plugins/src/main/kotlin/ScreenshotConventionPlugin.kt
@@ -0,0 +1,17 @@
+import io.github.takahirom.roborazzi.RoborazziExtension
+import org.gradle.api.Plugin
+import org.gradle.api.Project
+import org.gradle.kotlin.dsl.apply
+import org.gradle.kotlin.dsl.configure
+
+class ScreenshotConventionPlugin : Plugin {
+ override fun apply(target: Project) {
+ with(target) {
+ pluginManager.apply("io.github.takahirom.roborazzi")
+
+ extensions.configure {
+ outputDir.set(file("src/test/screenshots"))
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/build.gradle.kts b/build.gradle.kts
index 69274059..59d7e687 100644
--- a/build.gradle.kts
+++ b/build.gradle.kts
@@ -16,6 +16,7 @@ plugins {
alias(libs.plugins.kover)
alias(libs.plugins.ktlint) apply false
alias(libs.plugins.licensee) apply false
+ alias(libs.plugins.roborazzi) apply false
}
tasks.register("clean").configure {
diff --git a/gradle.properties b/gradle.properties
index 226d2d56..28cdd439 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -37,3 +37,5 @@ POM_DEVELOPER_NAME=amazonwebservices
POM_DEVELOPER_ORGANIZATION=Amazon Web Services
POM_DEVELOPER_ORGANIZATION_URL=http://aws.amazon.com
+# Verify roborazzy screenshots when running tests
+roborazzi.test.verify=true
\ No newline at end of file
diff --git a/gradle/libs.versions.toml b/gradle/libs.versions.toml
index e7fc7a7f..77948700 100644
--- a/gradle/libs.versions.toml
+++ b/gradle/libs.versions.toml
@@ -21,8 +21,8 @@ ktlint = "11.0.0"
licensee = "1.7.0"
lifecycle = "2.4.0"
mockk = "1.13.4"
-paparazzi = "1.3.5"
-robolectric = "4.9.2"
+robolectric = "4.14.1"
+roborazzi = "1.43.1"
serialization = "1.3.3"
tensorflow = "2.0.0"
tensorflow-support = "0.3.0"
@@ -80,6 +80,8 @@ test-junit = { module = "junit:junit", version.ref = "junit" }
test-kotest-assertions = { module = "io.kotest:kotest-assertions-core", version.ref = "kotest" }
test-mockk = { module = "io.mockk:mockk", version.ref = "mockk" }
test-robolectric = { module = "org.robolectric:robolectric", version.ref = "robolectric" }
+test-roborazzi = { module = "io.github.takahirom.roborazzi:roborazzi", version.ref = "roborazzi" }
+test-roborazzi-compose = { module = "io.github.takahirom.roborazzi:roborazzi-compose", version.ref = "roborazzi" }
test-turbine = { module = "app.cash.turbine:turbine", version.ref = "turbine" }
# Dependencies for convention plugins
@@ -89,6 +91,7 @@ plugin-kotlin-android = { module = "org.jetbrains.kotlin:kotlin-gradle-plugin",
plugin-kover = { module = "org.jetbrains.kotlinx:kover-gradle-plugin", version.ref = "kover" }
plugin-ktlint = { module = "org.jlleitschuh.gradle:ktlint-gradle", version.ref = "ktlint" }
plugin-licensee = { module = "app.cash.licensee:app.cash.licensee.gradle.plugin", version.ref = "licensee" }
+plugin-roborazzi = { module = "io.github.takahirom.roborazzi:io.github.takahirom.roborazzi.gradle.plugin", version.ref = "roborazzi" }
[bundles]
camera = ["androidx-camera-core", "androidx-camera-camera2", "androidx-camera-lifecycle"]
@@ -98,6 +101,8 @@ test = [
"test-junit",
"test-mockk",
"test-robolectric",
+ "test-roborazzi",
+ "test-roborazzi-compose",
"test-compose-junit",
"test-compose-manifest",
"test-coroutines",
@@ -114,4 +119,4 @@ kotlin-serialization = { id = "org.jetbrains.kotlin.plugin.serialization", versi
kover = { id = "org.jetbrains.kotlinx.kover", version.ref = "kover" }
ktlint = { id = "org.jlleitschuh.gradle.ktlint", version.ref = "ktlint" }
licensee = { id = "app.cash.licensee", version.ref = "licensee" }
-paparazzi = { id = "app.cash.paparazzi", version.ref = "paparazzi" }
+roborazzi = { id = "io.github.takahirom.roborazzi", version.ref="roborazzi" }
diff --git a/settings.gradle.kts b/settings.gradle.kts
index dd5b24a8..35916131 100644
--- a/settings.gradle.kts
+++ b/settings.gradle.kts
@@ -19,7 +19,6 @@ rootProject.name = "amplify-ui-android"
include(":liveness")
include(":authenticator")
include(":testing")
-include(":authenticator-screenshots")
// Enable typesafe accessor generation for cross-project references
enableFeaturePreview("TYPESAFE_PROJECT_ACCESSORS")
diff --git a/testing/build.gradle.kts b/testing/build.gradle.kts
index f7f16d80..606ea8a8 100644
--- a/testing/build.gradle.kts
+++ b/testing/build.gradle.kts
@@ -26,4 +26,6 @@ dependencies {
api(libs.bundles.test)
implementation(libs.bundles.compose)
+
+ implementation(libs.test.roborazzi)
}
diff --git a/testing/src/main/java/com/amplifyframework/ui/testing/ComposeTest.kt b/testing/src/main/java/com/amplifyframework/ui/testing/ComposeTest.kt
index 025abb29..2be74310 100644
--- a/testing/src/main/java/com/amplifyframework/ui/testing/ComposeTest.kt
+++ b/testing/src/main/java/com/amplifyframework/ui/testing/ComposeTest.kt
@@ -22,24 +22,32 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.test.junit4.createComposeRule
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.github.takahirom.roborazzi.RobolectricDeviceQualifiers
import org.junit.Before
import org.junit.Rule
import org.junit.runner.RunWith
-import org.robolectric.RobolectricTestRunner
+import org.robolectric.annotation.Config
+import org.robolectric.annotation.GraphicsMode
import org.robolectric.shadows.ShadowLog
-@RunWith(RobolectricTestRunner::class)
+@RunWith(AndroidJUnit4::class)
+@GraphicsMode(GraphicsMode.Mode.NATIVE)
+@Config(sdk = [33], qualifiers = RobolectricDeviceQualifiers.Pixel6)
abstract class ComposeTest {
@get:Rule
val composeTestRule = createComposeRule()
+ @get:Rule
+ val screenshotRule = ScreenshotRule(composeTestRule)
+
@Before
@Throws(Exception::class)
fun setupLogging() {
ShadowLog.stream = System.out // Redirect Logcat to console
}
- protected fun setContent(content: @Composable () -> Unit) = composeTestRule.setContent {
+ protected open fun setContent(content: @Composable () -> Unit) = composeTestRule.setContent {
Box(modifier = Modifier.fillMaxSize().verticalScroll(rememberScrollState())) {
content()
}
diff --git a/testing/src/main/java/com/amplifyframework/ui/testing/ScreenshotRule.kt b/testing/src/main/java/com/amplifyframework/ui/testing/ScreenshotRule.kt
new file mode 100644
index 00000000..3a620538
--- /dev/null
+++ b/testing/src/main/java/com/amplifyframework/ui/testing/ScreenshotRule.kt
@@ -0,0 +1,66 @@
+/*
+ * Copyright 2025 Amazon.com, Inc. or its affiliates. All Rights Reserved.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License").
+ * You may not use this file except in compliance with the License.
+ * A copy of the License is located at
+ *
+ * http://aws.amazon.com/apache2.0
+ *
+ * or in the "license" file accompanying this file. This file is distributed
+ * on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
+ * express or implied. See the License for the specific language governing
+ * permissions and limitations under the License.
+ */
+
+package com.amplifyframework.ui.testing
+
+import androidx.compose.ui.test.isRoot
+import androidx.compose.ui.test.junit4.ComposeTestRule
+import com.github.takahirom.roborazzi.RoborazziOptions
+import com.github.takahirom.roborazzi.captureRoboImage
+import java.io.File
+import org.junit.rules.TestRule
+import org.junit.runner.Description
+import org.junit.runners.model.Statement
+
+@Target(AnnotationTarget.FUNCTION)
+@Retention(AnnotationRetention.RUNTIME)
+annotation class ScreenshotTest
+
+/**
+ * Rule that captures a RoboRazzi screenshot only for annotated tests. To use this rule:
+ *
+ * 1. Extend the `ComposeTest` base class.
+ * 2. Write UI tests.
+ * 3. Annotate tests with `@ScreenshotTest` to have a screenshot automatically taken at the end of the test.
+ *
+ * Screenshot will be named "ClassName_function-name.png"
+ */
+class ScreenshotRule(val composeTestRule: ComposeTestRule) : TestRule {
+
+ private val options = RoborazziOptions(
+ compareOptions = RoborazziOptions.CompareOptions(
+ // Allow a 0.5% difference when comparing to allow for platform rendering differences
+ changeThreshold = 0.005f
+ )
+ )
+
+ override fun apply(base: Statement, description: Description): Statement = object : Statement() {
+ override fun evaluate() {
+ base.evaluate()
+ if (description.getAnnotation(ScreenshotTest::class.java) != null) {
+ composeTestRule.onNode(isRoot()).captureRoboImage(
+ file = File("src/test/screenshots", generateScreenshotName(description)),
+ roborazziOptions = options
+ )
+ }
+ }
+ }
+
+ private fun generateScreenshotName(description: Description): String {
+ val className = description.className.takeLastWhile { it != '.' }
+ val methodName = description.methodName.replace("\\s+".toRegex(), "-")
+ return "${className}_$methodName.png"
+ }
+}