From d673f51113128062971917c2ab2af7ecc9a1acd2 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Mon, 9 Dec 2024 17:07:27 +0200 Subject: [PATCH 1/7] Added 'disallow_password_messages_for_terms' and 'disallow_password_messages_error_text' to ClientConfiguration and handle them.| #2905 --- .../response/model/ClientConfiguration.kt | 24 ++++++++++++++++--- .../fragment/CreateMessageFragment.kt | 17 ++++++++++++- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt index 5bb976756f..145840e02b 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.api.retrofit.response.model @@ -15,7 +15,7 @@ import kotlinx.parcelize.Parcelize * @author Denys Bondarenko */ @Parcelize -data class ClientConfiguration constructor( +data class ClientConfiguration( @Expose val flags: List? = null, @SerializedName("custom_keyserver_url") @Expose val customKeyserverUrl: String? = null, @@ -30,7 +30,11 @@ data class ClientConfiguration constructor( @SerializedName("enforce_keygen_expire_months") @Expose val enforceKeygenExpireMonths: Int? = null, @SerializedName("in_memory_pass_phrase_session_length") - @Expose val inMemoryPassPhraseSessionLength: Int? = null + @Expose val inMemoryPassPhraseSessionLength: Int? = null, + @SerializedName("disallow_password_messages_for_terms") + @Expose val disallowPasswordMessagesForTerms: List? = null, + @SerializedName("disallow_password_messages_error_text") + @Expose val disallowPasswordMessagesErrorText: String? = null, ) : Parcelable { val inMemoryPassPhraseSessionLengthNormalized: Int? @@ -203,6 +207,20 @@ data class ClientConfiguration constructor( return hasProperty(ConfigurationProperty.HIDE_ARMOR_META) } + /** + * With this option, the app must check the subjects of composed password-protected email + * messages for specified strings(defined in [disallowPasswordMessagesForTerms]). + * If any configured terms are found, the application should display an error message + * (defined in [disallowPasswordMessagesErrorText]) + * indicating that the password encryption method is incompatible with the composed message. + * + * ref https://github.com/FlowCrypt/flowcrypt-android/issues/2905 + */ + fun hasRestrictionForPasswordProtectedMessages(): Boolean { + return !disallowPasswordMessagesForTerms.isNullOrEmpty() + && !disallowPasswordMessagesErrorText.isNullOrEmpty() + } + fun hasProperty(configurationProperty: ConfigurationProperty): Boolean { return flags?.firstOrNull { it == configurationProperty } != null } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt index 1f35e5ce74..5ef2c29b08 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt @@ -1,6 +1,6 @@ /* * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com - * Contributors: DenBond7 + * Contributors: denbond7 */ package com.flowcrypt.email.ui.activity.fragment @@ -1569,6 +1569,21 @@ class CreateMessageFragment : BaseFragment(), ) return false } + + if (account?.clientConfiguration?.hasRestrictionForPasswordProtectedMessages() == true) { + val terms = account?.clientConfiguration?.disallowPasswordMessagesForTerms ?: emptyList() + if (binding?.editTextEmailSubject?.text?.contains( + terms.joinToString("|").toRegex(RegexOption.IGNORE_CASE) + ) == true + ) { + showInfoDialog( + dialogTitle = "", + dialogMsg = account?.clientConfiguration?.disallowPasswordMessagesErrorText, + useLinkify = true + ) + return false + } + } } } if (binding?.editTextEmailSubject?.text?.isEmpty() == true) { From 775c476c2606d43bffe79962ed4643a302666974 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Mon, 9 Dec 2024 17:54:47 +0200 Subject: [PATCH 2/7] Added some test.| #2905 --- ...eenPasswordProtectedDisallowedTermsTest.kt | 85 +++++++++++++++++++ ...ordProtectedDisallowedTermsNullFlowTest.kt | 68 +++++++++++++++ 2 files changed, 153 insertions(+) create mode 100644 FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt create mode 100644 FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt new file mode 100644 index 0000000000..c7d8354cd0 --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt @@ -0,0 +1,85 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.ui.gmailapi.passwordprotected + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.closeSoftKeyboard +import androidx.test.espresso.action.ViewActions.pressImeActionButton +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.action.ViewActions.scrollTo +import androidx.test.espresso.action.ViewActions.typeText +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import com.flowcrypt.email.R +import com.flowcrypt.email.TestConstants +import com.flowcrypt.email.database.entity.AccountEntity +import com.flowcrypt.email.matchers.CustomMatchers.Companion.withTextViewDrawable +import com.flowcrypt.email.matchers.TextViewDrawableMatcher +import com.flowcrypt.email.rules.FlowCryptMockWebServerRule +import com.flowcrypt.email.ui.base.BaseComposeGmailFlow +import okhttp3.mockwebserver.Dispatcher +import okhttp3.mockwebserver.MockResponse +import okhttp3.mockwebserver.RecordedRequest +import org.junit.Before + +/** + * @author Denys Bondarenko + */ +open class BaseComposeScreenPasswordProtectedDisallowedTermsTest( + accountEntity: AccountEntity = BASE_ACCOUNT_ENTITY +) : BaseComposeGmailFlow(accountEntity) { + override val mockWebServerRule = + FlowCryptMockWebServerRule(TestConstants.MOCK_WEB_SERVER_PORT, object : Dispatcher() { + override fun dispatch(request: RecordedRequest): MockResponse { + return handleCommonAPICalls(request) + } + }) + + @Before + fun setUp() { + onView(withId(R.id.editTextEmailAddress)) + .perform( + typeText(TestConstants.RECIPIENT_WITHOUT_PUBLIC_KEY_ON_ATTESTER), + pressImeActionButton(), + closeSoftKeyboard() + ) + //need to leave focus from 'To' field. move the focus to the next view + onView(withId(R.id.editTextEmailSubject)) + .perform(scrollTo(), click()) + + onView(withId(R.id.btnSetWebPortalPassword)) + .check(matches(isDisplayed())) + .check(matches(withText(getResString(R.string.tap_to_protect_with_web_portal_password)))) + .check( + matches( + withTextViewDrawable( + resourceId = R.drawable.ic_password_not_protected_white_24, + drawablePosition = TextViewDrawableMatcher.DrawablePosition.LEFT + ) + ) + ).perform(click()) + + onView(withId(R.id.eTPassphrase)) + .perform( + replaceText(PASSWORD), + closeSoftKeyboard() + ) + + onView(withId(R.id.btSetPassword)) + .perform(click()) + + onView(withId(R.id.menuActionSend)) + .check(matches(isDisplayed())) + .perform(click()) + } + + companion object { + private const val PASSWORD = "Qwerty1234@" + } +} \ No newline at end of file diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt new file mode 100644 index 0000000000..b3cb10bead --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt @@ -0,0 +1,68 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.ui.gmailapi.passwordprotected + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import com.flowcrypt.email.R +import com.flowcrypt.email.junit.annotations.OutgoingMessageConfiguration +import com.flowcrypt.email.rules.AddRecipientsToDatabaseRule +import com.flowcrypt.email.rules.ClearAppSettingsRule +import com.flowcrypt.email.rules.GrantPermissionRuleChooser +import com.flowcrypt.email.rules.RetryRule +import com.flowcrypt.email.rules.ScreenshotTestRule +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +/** + * @author Denys Bondarenko + */ +@MediumTest +@RunWith(AndroidJUnit4::class) +@OutgoingMessageConfiguration( + to = [], + cc = [], + bcc = [], + message = "", + subject = "", + isNew = true +) +class ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest : + BaseComposeScreenPasswordProtectedDisallowedTermsTest( + ACCOUNT_ENTITY_WITH_MISSING_OPTIONAL_PROPERTIES + ) { + + @get:Rule + var ruleChain: TestRule = + RuleChain.outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) + .around(GrantPermissionRuleChooser.grant(android.Manifest.permission.POST_NOTIFICATIONS)) + .around(mockWebServerRule) + .around(addAccountToDatabaseRule) + .around(addPrivateKeyToDatabaseRule) + .around(AddRecipientsToDatabaseRule(prepareRecipientsForTest())) + .around(addLabelsToDatabaseRule) + .around(activityScenarioRule) + .around(ScreenshotTestRule()) + + @Test + fun testMissingOptionalPropertiesInClientConfiguration() { + onView( + withText(getResString(R.string.text_must_not_be_empty, getResString(R.string.prompt_subject))) + ).check(matches(isDisplayed())) + } + + companion object { + val ACCOUNT_ENTITY_WITH_MISSING_OPTIONAL_PROPERTIES = BASE_ACCOUNT_ENTITY.copy() + } +} From 2e07234227c7c4429e38558a94d599942c6a5bec Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Mon, 9 Dec 2024 18:01:26 +0200 Subject: [PATCH 3/7] Added some tests.| #2905 --- .../email/ui/base/BaseComposeGmailFlow.kt | 2 + ...eenPasswordProtectedDisallowedTermsTest.kt | 19 +++- ...DisallowedTermsErrorTextMissingFlowTest.kt | 81 ++++++++++++++ ...asswordProtectedDisallowedTermsFlowTest.kt | 100 ++++++++++++++++++ ...ctedDisallowedTermsMissingTermsFlowTest.kt | 81 ++++++++++++++ ...ordProtectedDisallowedTermsNullFlowTest.kt | 9 +- .../fragment/CreateMessageFragment.kt | 35 +++--- 7 files changed, 306 insertions(+), 21 deletions(-) create mode 100644 FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsErrorTextMissingFlowTest.kt create mode 100644 FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsFlowTest.kt create mode 100644 FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsMissingTermsFlowTest.kt diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseComposeGmailFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseComposeGmailFlow.kt index abfe34640b..08f780a37c 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseComposeGmailFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseComposeGmailFlow.kt @@ -503,6 +503,8 @@ abstract class BaseComposeGmailFlow(accountEntity: AccountEntity = BASE_ACCOUNT_ Thread.sleep(outgoingMessageConfiguration.timeoutBeforeMovingToComposeInMilliseconds) } + Thread.sleep(1000) + //open the compose screen onView(withId(R.id.floatActionButtonCompose)) .check(matches(isDisplayed())) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt index c7d8354cd0..9f67526f5f 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt @@ -51,7 +51,7 @@ open class BaseComposeScreenPasswordProtectedDisallowedTermsTest( ) //need to leave focus from 'To' field. move the focus to the next view onView(withId(R.id.editTextEmailSubject)) - .perform(scrollTo(), click()) + .perform(scrollTo(), replaceText(SUBJECT), click()) onView(withId(R.id.btnSetWebPortalPassword)) .check(matches(isDisplayed())) @@ -73,13 +73,22 @@ open class BaseComposeScreenPasswordProtectedDisallowedTermsTest( onView(withId(R.id.btSetPassword)) .perform(click()) - - onView(withId(R.id.menuActionSend)) - .check(matches(isDisplayed())) - .perform(click()) } companion object { private const val PASSWORD = "Qwerty1234@" + const val URL = "https://flowcrypt.com" + const val SUBJECT = + "Android developers from Europe and across the globe will converge this year at droidcon in London" + const val ERROR_TEXT = + "Password-protected messages are disabled, please check $URL" + + val TERMS = listOf( + "Droid", + "Android", + "ANDROID", + "android", + "AnDroid" + ) } } \ No newline at end of file diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsErrorTextMissingFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsErrorTextMissingFlowTest.kt new file mode 100644 index 0000000000..2ebb27b832 --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsErrorTextMissingFlowTest.kt @@ -0,0 +1,81 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.ui.gmailapi.passwordprotected + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import com.flowcrypt.email.R +import com.flowcrypt.email.junit.annotations.FlowCryptTestSettings +import com.flowcrypt.email.junit.annotations.OutgoingMessageConfiguration +import com.flowcrypt.email.rules.AddRecipientsToDatabaseRule +import com.flowcrypt.email.rules.ClearAppSettingsRule +import com.flowcrypt.email.rules.GrantPermissionRuleChooser +import com.flowcrypt.email.rules.RetryRule +import com.flowcrypt.email.rules.ScreenshotTestRule +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +/** + * @author Denys Bondarenko + */ +@MediumTest +@RunWith(AndroidJUnit4::class) +@FlowCryptTestSettings(useCommonIdling = false) +@OutgoingMessageConfiguration( + to = [], + cc = [], + bcc = [], + message = "", + subject = "", + isNew = true +) +class ComposeScreenPasswordProtectedDisallowedTermsErrorTextMissingFlowTest : + BaseComposeScreenPasswordProtectedDisallowedTermsTest( + ACCOUNT_ENTITY_WITH_MISSING_ERROR_TEXT + ) { + + @get:Rule + var ruleChain: TestRule = + RuleChain.outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) + .around(GrantPermissionRuleChooser.grant(android.Manifest.permission.POST_NOTIFICATIONS)) + .around(mockWebServerRule) + .around(addAccountToDatabaseRule) + .around(addPrivateKeyToDatabaseRule) + .around(AddRecipientsToDatabaseRule(prepareRecipientsForTest())) + .around(addLabelsToDatabaseRule) + .around(activityScenarioRule) + .around(ScreenshotTestRule()) + + @Test + fun testMissingOptionalPropertiesInClientConfiguration() { + onView(withId(R.id.menuActionSend)) + .check(matches(isDisplayed())) + .perform(click()) + onView( + withText(getResString(R.string.sending_message_must_not_be_empty)) + ).check(matches(isDisplayed())) + } + + companion object { + val ACCOUNT_ENTITY_WITH_MISSING_ERROR_TEXT = + BASE_ACCOUNT_ENTITY.copy( + clientConfiguration = BASE_ACCOUNT_ENTITY.clientConfiguration?.copy( + disallowPasswordMessagesErrorText = null, + disallowPasswordMessagesForTerms = TERMS + ) + ) + } +} diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsFlowTest.kt new file mode 100644 index 0000000000..f68cac802c --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsFlowTest.kt @@ -0,0 +1,100 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.ui.gmailapi.passwordprotected + +import android.app.Instrumentation +import android.content.Intent +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.action.ViewActions.openLinkWithText +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.intent.Intents +import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction +import androidx.test.espresso.intent.matcher.IntentMatchers.hasData +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import com.flowcrypt.email.R +import com.flowcrypt.email.junit.annotations.FlowCryptTestSettings +import com.flowcrypt.email.junit.annotations.OutgoingMessageConfiguration +import com.flowcrypt.email.rules.AddRecipientsToDatabaseRule +import com.flowcrypt.email.rules.ClearAppSettingsRule +import com.flowcrypt.email.rules.GrantPermissionRuleChooser +import com.flowcrypt.email.rules.RetryRule +import com.flowcrypt.email.rules.ScreenshotTestRule +import org.hamcrest.Matchers.allOf +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +/** + * @author Denys Bondarenko + */ +@MediumTest +@RunWith(AndroidJUnit4::class) +@FlowCryptTestSettings(useCommonIdling = false, useIntents = true) +@OutgoingMessageConfiguration( + to = [], + cc = [], + bcc = [], + message = "", + subject = "", + isNew = true +) +class ComposeScreenPasswordProtectedDisallowedTermsFlowTest : + BaseComposeScreenPasswordProtectedDisallowedTermsTest( + ACCOUNT_ENTITY_WITH_EXISTING_OPTIONAL_PARAMETERS + ) { + + @get:Rule + var ruleChain: TestRule = + RuleChain.outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) + .around(GrantPermissionRuleChooser.grant(android.Manifest.permission.POST_NOTIFICATIONS)) + .around(mockWebServerRule) + .around(addAccountToDatabaseRule) + .around(addPrivateKeyToDatabaseRule) + .around(AddRecipientsToDatabaseRule(prepareRecipientsForTest())) + .around(addLabelsToDatabaseRule) + .around(activityScenarioRule) + .around(ScreenshotTestRule()) + + @Test + fun testDialogWithErrorText() { + onView(withId(R.id.menuActionSend)) + .check(matches(isDisplayed())) + .perform(click()) + + isDialogWithTextDisplayed( + decorView, + ERROR_TEXT + ) + + //test that URL is openable + val expectingIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(URL)) + //mocking intent to prevent actual navigation during test + Intents.intending(expectingIntent).respondWith(Instrumentation.ActivityResult(0, null)) + //performing action + onView(withText(ERROR_TEXT)) + .perform(openLinkWithText(URL)) + //asserting our expected intent was recorded + Intents.intended(expectingIntent) + } + + companion object { + val ACCOUNT_ENTITY_WITH_EXISTING_OPTIONAL_PARAMETERS = + BASE_ACCOUNT_ENTITY.copy( + clientConfiguration = BASE_ACCOUNT_ENTITY.clientConfiguration?.copy( + disallowPasswordMessagesErrorText = ERROR_TEXT, + disallowPasswordMessagesForTerms = TERMS + ) + ) + } +} diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsMissingTermsFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsMissingTermsFlowTest.kt new file mode 100644 index 0000000000..da39f4f5c6 --- /dev/null +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsMissingTermsFlowTest.kt @@ -0,0 +1,81 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.ui.gmailapi.passwordprotected + +import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.matches +import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId +import androidx.test.espresso.matcher.ViewMatchers.withText +import androidx.test.ext.junit.runners.AndroidJUnit4 +import androidx.test.filters.MediumTest +import com.flowcrypt.email.R +import com.flowcrypt.email.junit.annotations.FlowCryptTestSettings +import com.flowcrypt.email.junit.annotations.OutgoingMessageConfiguration +import com.flowcrypt.email.rules.AddRecipientsToDatabaseRule +import com.flowcrypt.email.rules.ClearAppSettingsRule +import com.flowcrypt.email.rules.GrantPermissionRuleChooser +import com.flowcrypt.email.rules.RetryRule +import com.flowcrypt.email.rules.ScreenshotTestRule +import org.junit.Rule +import org.junit.Test +import org.junit.rules.RuleChain +import org.junit.rules.TestRule +import org.junit.runner.RunWith + +/** + * @author Denys Bondarenko + */ +@MediumTest +@RunWith(AndroidJUnit4::class) +@FlowCryptTestSettings(useCommonIdling = false) +@OutgoingMessageConfiguration( + to = [], + cc = [], + bcc = [], + message = "", + subject = "", + isNew = true +) +class ComposeScreenPasswordProtectedDisallowedTermsMissingTermsFlowTest : + BaseComposeScreenPasswordProtectedDisallowedTermsTest( + ACCOUNT_ENTITY_WITH_MISSING_TERMS + ) { + + @get:Rule + var ruleChain: TestRule = + RuleChain.outerRule(RetryRule.DEFAULT) + .around(ClearAppSettingsRule()) + .around(GrantPermissionRuleChooser.grant(android.Manifest.permission.POST_NOTIFICATIONS)) + .around(mockWebServerRule) + .around(addAccountToDatabaseRule) + .around(addPrivateKeyToDatabaseRule) + .around(AddRecipientsToDatabaseRule(prepareRecipientsForTest())) + .around(addLabelsToDatabaseRule) + .around(activityScenarioRule) + .around(ScreenshotTestRule()) + + @Test + fun testMissingOptionalPropertiesInClientConfiguration() { + onView(withId(R.id.menuActionSend)) + .check(matches(isDisplayed())) + .perform(click()) + onView( + withText(getResString(R.string.sending_message_must_not_be_empty)) + ).check(matches(isDisplayed())) + } + + companion object { + val ACCOUNT_ENTITY_WITH_MISSING_TERMS = + BASE_ACCOUNT_ENTITY.copy( + clientConfiguration = BASE_ACCOUNT_ENTITY.clientConfiguration?.copy( + disallowPasswordMessagesErrorText = ERROR_TEXT, + disallowPasswordMessagesForTerms = null + ) + ) + } +} diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt index b3cb10bead..5953a172d3 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt @@ -6,12 +6,15 @@ package com.flowcrypt.email.ui.gmailapi.passwordprotected import androidx.test.espresso.Espresso.onView +import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed +import androidx.test.espresso.matcher.ViewMatchers.withId import androidx.test.espresso.matcher.ViewMatchers.withText import androidx.test.ext.junit.runners.AndroidJUnit4 import androidx.test.filters.MediumTest import com.flowcrypt.email.R +import com.flowcrypt.email.junit.annotations.FlowCryptTestSettings import com.flowcrypt.email.junit.annotations.OutgoingMessageConfiguration import com.flowcrypt.email.rules.AddRecipientsToDatabaseRule import com.flowcrypt.email.rules.ClearAppSettingsRule @@ -29,6 +32,7 @@ import org.junit.runner.RunWith */ @MediumTest @RunWith(AndroidJUnit4::class) +@FlowCryptTestSettings(useCommonIdling = false) @OutgoingMessageConfiguration( to = [], cc = [], @@ -57,8 +61,11 @@ class ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest : @Test fun testMissingOptionalPropertiesInClientConfiguration() { + onView(withId(R.id.menuActionSend)) + .check(matches(isDisplayed())) + .perform(click()) onView( - withText(getResString(R.string.text_must_not_be_empty, getResString(R.string.prompt_subject))) + withText(getResString(R.string.sending_message_must_not_be_empty)) ).check(matches(isDisplayed())) } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt index 5ef2c29b08..874a0a0149 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt @@ -1538,6 +1538,7 @@ class CreateMessageFragment : BaseFragment(), return false } + var isWebPortalPasswordEnabled = false if (composeMsgViewModel.msgEncryptionType === MessageEncryptionType.ENCRYPTED) { if (composeMsgViewModel.allRecipients.any { it.value.isUpdating }) { toast(R.string.please_wait_while_information_about_recipients_will_be_updated) @@ -1550,6 +1551,7 @@ class CreateMessageFragment : BaseFragment(), val password = composeMsgViewModel.webPortalPasswordStateFlow.value if (password.isNotEmpty()) { + isWebPortalPasswordEnabled = true val keysStorage = KeysStorageImpl.getInstance(requireContext()) if (keysStorage.hasPassphrase(Passphrase(password.toString().toCharArray()))) { showInfoDialog( @@ -1569,21 +1571,6 @@ class CreateMessageFragment : BaseFragment(), ) return false } - - if (account?.clientConfiguration?.hasRestrictionForPasswordProtectedMessages() == true) { - val terms = account?.clientConfiguration?.disallowPasswordMessagesForTerms ?: emptyList() - if (binding?.editTextEmailSubject?.text?.contains( - terms.joinToString("|").toRegex(RegexOption.IGNORE_CASE) - ) == true - ) { - showInfoDialog( - dialogTitle = "", - dialogMsg = account?.clientConfiguration?.disallowPasswordMessagesErrorText, - useLinkify = true - ) - return false - } - } } } if (binding?.editTextEmailSubject?.text?.isEmpty() == true) { @@ -1596,6 +1583,24 @@ class CreateMessageFragment : BaseFragment(), binding?.editTextEmailSubject?.requestFocus() return false } + + if (isWebPortalPasswordEnabled && + account?.clientConfiguration?.hasRestrictionForPasswordProtectedMessages() == true + ) { + val terms = account?.clientConfiguration?.disallowPasswordMessagesForTerms ?: emptyList() + if (binding?.editTextEmailSubject?.text?.contains( + terms.joinToString("|").toRegex(RegexOption.IGNORE_CASE) + ) == true + ) { + showInfoDialog( + dialogTitle = "", + dialogMsg = account?.clientConfiguration?.disallowPasswordMessagesErrorText, + useLinkify = true + ) + return false + } + } + if (composeMsgViewModel.attachmentsStateFlow.value.isEmpty() && binding?.editTextEmailMessage?.text?.isEmpty() == true) { showInfoSnackbar( binding?.editTextEmailMessage, From 94ae2ce01ce9cb85af54bd10a0d6a901b60a4cbf Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Tue, 10 Dec 2024 16:45:45 +0200 Subject: [PATCH 4/7] Improved searching pattern. Improved tests.| #2905 --- ...eenPasswordProtectedDisallowedTermsTest.kt | 26 +++++--- ...asswordProtectedDisallowedTermsFlowTest.kt | 65 ++++++++++++++----- .../response/model/ClientConfiguration.kt | 11 ++++ .../fragment/CreateMessageFragment.kt | 7 +- .../response/model/ClientConfigurationTest.kt | 48 ++++++++++++++ 5 files changed, 128 insertions(+), 29 deletions(-) create mode 100644 FlowCrypt/src/test/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfigurationTest.kt diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt index 9f67526f5f..7dce4dd677 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt @@ -51,7 +51,7 @@ open class BaseComposeScreenPasswordProtectedDisallowedTermsTest( ) //need to leave focus from 'To' field. move the focus to the next view onView(withId(R.id.editTextEmailSubject)) - .perform(scrollTo(), replaceText(SUBJECT), click()) + .perform(scrollTo(), click(), replaceText(MATCHING_SUBJECTS.first())) onView(withId(R.id.btnSetWebPortalPassword)) .check(matches(isDisplayed())) @@ -78,17 +78,27 @@ open class BaseComposeScreenPasswordProtectedDisallowedTermsTest( companion object { private const val PASSWORD = "Qwerty1234@" const val URL = "https://flowcrypt.com" - const val SUBJECT = - "Android developers from Europe and across the globe will converge this year at droidcon in London" + + val MATCHING_SUBJECTS = listOf( + "[Classification: Data Control: Internal Data Control] Quarter results", + "Conference information [Classification: Data Control: Internal Data Control]", + "[Classification: Data Control: Internal Data Control]", + "Conference information [Classification: Data Control: Internal Data Control] Android" + ) + + val NON_MATCHING_SUBJECTS = listOf( + "[Classification: Data Control: Internal Data Control]! Quarter results", + "Conference information [Classification: Data Control: Internal Data Control])", + "([Classification: Data Control: Internal Data Control]", + "Conference information ([Classification: Data Control: Internal Data Control]) Android" + ) + const val ERROR_TEXT = "Password-protected messages are disabled, please check $URL" val TERMS = listOf( - "Droid", - "Android", - "ANDROID", - "android", - "AnDroid" + "vendor", + "[Classification: Data Control: Internal Data Control]", ) } } \ No newline at end of file diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsFlowTest.kt index f68cac802c..323337c07d 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsFlowTest.kt @@ -10,6 +10,8 @@ import android.content.Intent import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click import androidx.test.espresso.action.ViewActions.openLinkWithText +import androidx.test.espresso.action.ViewActions.replaceText +import androidx.test.espresso.action.ViewActions.scrollTo import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.intent.Intents import androidx.test.espresso.intent.matcher.IntentMatchers.hasAction @@ -68,24 +70,55 @@ class ComposeScreenPasswordProtectedDisallowedTermsFlowTest : @Test fun testDialogWithErrorText() { - onView(withId(R.id.menuActionSend)) - .check(matches(isDisplayed())) - .perform(click()) + intentsRelease() - isDialogWithTextDisplayed( - decorView, - ERROR_TEXT - ) + MATCHING_SUBJECTS.forEach { subject -> + onView(withId(R.id.editTextEmailSubject)) + .perform(scrollTo(), click(), replaceText(subject)) - //test that URL is openable - val expectingIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(URL)) - //mocking intent to prevent actual navigation during test - Intents.intending(expectingIntent).respondWith(Instrumentation.ActivityResult(0, null)) - //performing action - onView(withText(ERROR_TEXT)) - .perform(openLinkWithText(URL)) - //asserting our expected intent was recorded - Intents.intended(expectingIntent) + onView(withId(R.id.menuActionSend)) + .check(matches(isDisplayed())) + .perform(click()) + + isDialogWithTextDisplayed( + decorView, + ERROR_TEXT + ) + + Intents.init() + //test that URL is openable + val expectingIntent = allOf(hasAction(Intent.ACTION_VIEW), hasData(URL)) + //mocking intent to prevent actual navigation during test + Intents.intending(expectingIntent).respondWith(Instrumentation.ActivityResult(0, null)) + //performing action + onView(withText(ERROR_TEXT)) + .perform(openLinkWithText(URL)) + //asserting our expected intent was recorded + Intents.intended(expectingIntent) + Thread.sleep(1000) + Intents.release() + + onView(withId(android.R.id.button1)) + .check(matches(isDisplayed())) + .perform(click()) + } + } + + @Test + fun testDialogWithoutErrorText() { + NON_MATCHING_SUBJECTS.forEach { subject -> + onView(withId(R.id.editTextEmailSubject)) + .perform(scrollTo(), click(), replaceText(subject)) + Thread.sleep(1000) + + onView(withId(R.id.menuActionSend)) + .check(matches(isDisplayed())) + .perform(click()) + + onView( + withText(getResString(R.string.sending_message_must_not_be_empty)) + ).check(matches(isDisplayed())) + } } companion object { diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt index 145840e02b..e3cbd44caa 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt @@ -225,6 +225,17 @@ data class ClientConfiguration( return flags?.firstOrNull { it == configurationProperty } != null } + fun getDisallowPasswordMessagesForTermsRegex(): Regex? { + return disallowPasswordMessagesForTerms?.joinToString( + separator = "|", + prefix = "(", + postfix = ")" + ) { + val escapedTerm = Regex.escape(it) + "(\\s$escapedTerm\\s|^$escapedTerm\\s|\\s$escapedTerm$|^$escapedTerm$)" + }?.toRegex(setOf(RegexOption.IGNORE_CASE)) + } + @Parcelize enum class ConfigurationProperty : Parcelable { NO_PRV_CREATE, diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt index 874a0a0149..5c14718003 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/ui/activity/fragment/CreateMessageFragment.kt @@ -1587,11 +1587,8 @@ class CreateMessageFragment : BaseFragment(), if (isWebPortalPasswordEnabled && account?.clientConfiguration?.hasRestrictionForPasswordProtectedMessages() == true ) { - val terms = account?.clientConfiguration?.disallowPasswordMessagesForTerms ?: emptyList() - if (binding?.editTextEmailSubject?.text?.contains( - terms.joinToString("|").toRegex(RegexOption.IGNORE_CASE) - ) == true - ) { + val termsRegex = account?.clientConfiguration?.getDisallowPasswordMessagesForTermsRegex() + if (termsRegex?.find(binding?.editTextEmailSubject?.text ?: "") != null) { showInfoDialog( dialogTitle = "", dialogMsg = account?.clientConfiguration?.disallowPasswordMessagesErrorText, diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfigurationTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfigurationTest.kt new file mode 100644 index 0000000000..d047c30946 --- /dev/null +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfigurationTest.kt @@ -0,0 +1,48 @@ +/* + * © 2016-present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com + * Contributors: denbond7 + */ + +package com.flowcrypt.email.api.retrofit.response.model + +import org.junit.Assert.assertNotNull +import org.junit.Assert.assertNull +import org.junit.Test + +/** + * @author Denys Bondarenko + */ +class ClientConfigurationTest { + @Test + fun testGetDisallowPasswordMessagesForTermsRegex() { + val clientConfiguration = ClientConfiguration( + disallowPasswordMessagesForTerms = listOf( + "vendor", + "[Classification: Data Control: Internal Data Control]", + ) + ) + + val regex = requireNotNull(clientConfiguration.getDisallowPasswordMessagesForTermsRegex()) + + val matchingSubjects = listOf( + "[Classification: Data Control: Internal Data Control] Quarter results", + "Conference information [Classification: Data Control: Internal Data Control]", + "[Classification: Data Control: Internal Data Control]", + "Conference information [Classification: Data Control: Internal Data Control] Android" + ) + + val nonMatchingSubjects = listOf( + "[Classification: Data Control: Internal Data Control]! Quarter results", + "Conference information [Classification: Data Control: Internal Data Control])", + "([Classification: Data Control: Internal Data Control]", + "Conference information ([Classification: Data Control: Internal Data Control]) Android" + ) + matchingSubjects.forEach { subject -> + assertNotNull("Exception in :$subject", regex.find(subject)) + } + + nonMatchingSubjects.forEach { subject -> + assertNull("Exception in :$subject", regex.find(subject)) + } + } +} \ No newline at end of file From 4e08f5422fb2b2a296077a2fcf549a7d643c2838 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Tue, 10 Dec 2024 17:14:38 +0200 Subject: [PATCH 5/7] wip --- .../java/com/flowcrypt/email/ui/base/BaseComposeGmailFlow.kt | 2 -- 1 file changed, 2 deletions(-) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseComposeGmailFlow.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseComposeGmailFlow.kt index 08f780a37c..abfe34640b 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseComposeGmailFlow.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/base/BaseComposeGmailFlow.kt @@ -503,8 +503,6 @@ abstract class BaseComposeGmailFlow(accountEntity: AccountEntity = BASE_ACCOUNT_ Thread.sleep(outgoingMessageConfiguration.timeoutBeforeMovingToComposeInMilliseconds) } - Thread.sleep(1000) - //open the compose screen onView(withId(R.id.floatActionButtonCompose)) .check(matches(isDisplayed())) From f1f79f3864d3879b06d1a56e5bdc45ad3cceaa14 Mon Sep 17 00:00:00 2001 From: DenBond7 Date: Tue, 10 Dec 2024 19:48:38 +0200 Subject: [PATCH 6/7] wip --- ...swordProtectedDisallowedTermsErrorTextMissingFlowTest.kt | 5 ++--- ...nPasswordProtectedDisallowedTermsMissingTermsFlowTest.kt | 5 ++--- ...oseScreenPasswordProtectedDisallowedTermsNullFlowTest.kt | 6 +++--- 3 files changed, 7 insertions(+), 9 deletions(-) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsErrorTextMissingFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsErrorTextMissingFlowTest.kt index 2ebb27b832..85f8459b15 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsErrorTextMissingFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsErrorTextMissingFlowTest.kt @@ -7,6 +7,7 @@ package com.flowcrypt.email.ui.gmailapi.passwordprotected import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId @@ -64,9 +65,7 @@ class ComposeScreenPasswordProtectedDisallowedTermsErrorTextMissingFlowTest : onView(withId(R.id.menuActionSend)) .check(matches(isDisplayed())) .perform(click()) - onView( - withText(getResString(R.string.sending_message_must_not_be_empty)) - ).check(matches(isDisplayed())) + onView(withText(ERROR_TEXT)).check(doesNotExist()) } companion object { diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsMissingTermsFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsMissingTermsFlowTest.kt index da39f4f5c6..ea2e7bd237 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsMissingTermsFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsMissingTermsFlowTest.kt @@ -7,6 +7,7 @@ package com.flowcrypt.email.ui.gmailapi.passwordprotected import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId @@ -64,9 +65,7 @@ class ComposeScreenPasswordProtectedDisallowedTermsMissingTermsFlowTest : onView(withId(R.id.menuActionSend)) .check(matches(isDisplayed())) .perform(click()) - onView( - withText(getResString(R.string.sending_message_must_not_be_empty)) - ).check(matches(isDisplayed())) + onView(withText(ERROR_TEXT)).check(doesNotExist()) } companion object { diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt index 5953a172d3..a190aae148 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest.kt @@ -7,6 +7,7 @@ package com.flowcrypt.email.ui.gmailapi.passwordprotected import androidx.test.espresso.Espresso.onView import androidx.test.espresso.action.ViewActions.click +import androidx.test.espresso.assertion.ViewAssertions.doesNotExist import androidx.test.espresso.assertion.ViewAssertions.matches import androidx.test.espresso.matcher.ViewMatchers.isDisplayed import androidx.test.espresso.matcher.ViewMatchers.withId @@ -64,9 +65,8 @@ class ComposeScreenPasswordProtectedDisallowedTermsNullFlowTest : onView(withId(R.id.menuActionSend)) .check(matches(isDisplayed())) .perform(click()) - onView( - withText(getResString(R.string.sending_message_must_not_be_empty)) - ).check(matches(isDisplayed())) + + onView(withText(ERROR_TEXT)).check(doesNotExist()) } companion object { From 006186c9eff88d0591c014bf7913386264bb8615 Mon Sep 17 00:00:00 2001 From: Denys Bondarenko Date: Wed, 11 Dec 2024 20:20:46 +0200 Subject: [PATCH 7/7] wip --- ...eScreenPasswordProtectedDisallowedTermsTest.kt | 15 +++++++++------ .../response/model/ClientConfiguration.kt | 12 +++++++++--- .../response/model/ClientConfigurationTest.kt | 15 +++++++++------ 3 files changed, 27 insertions(+), 15 deletions(-) diff --git a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt index 7dce4dd677..129f48cc93 100644 --- a/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt +++ b/FlowCrypt/src/androidTest/java/com/flowcrypt/email/ui/gmailapi/passwordprotected/BaseComposeScreenPasswordProtectedDisallowedTermsTest.kt @@ -83,21 +83,24 @@ open class BaseComposeScreenPasswordProtectedDisallowedTermsTest( "[Classification: Data Control: Internal Data Control] Quarter results", "Conference information [Classification: Data Control: Internal Data Control]", "[Classification: Data Control: Internal Data Control]", - "Conference information [Classification: Data Control: Internal Data Control] Android" + "aaaa[Classification: Data Control: Internal Data Control]bbb", + "[droid]", + "check -droid- case", ) val NON_MATCHING_SUBJECTS = listOf( - "[Classification: Data Control: Internal Data Control]! Quarter results", - "Conference information [Classification: Data Control: Internal Data Control])", - "([Classification: Data Control: Internal Data Control]", - "Conference information ([Classification: Data Control: Internal Data Control]) Android" + "[1Classification: Data Control: Internal Data Control] Quarter results", + "Conference information [1Classification: Data Control: Internal Data Control]", + "[1Classification: Data Control: Internal Data Control]", + "aaaa[1Classification: Data Control: Internal Data Control]bbb", + "Microdroid androids", ) const val ERROR_TEXT = "Password-protected messages are disabled, please check $URL" val TERMS = listOf( - "vendor", + "droid", "[Classification: Data Control: Internal Data Control]", ) } diff --git a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt index e3cbd44caa..e46e688d19 100644 --- a/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt +++ b/FlowCrypt/src/main/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfiguration.kt @@ -226,13 +226,19 @@ data class ClientConfiguration( } fun getDisallowPasswordMessagesForTermsRegex(): Regex? { + val startAndEntWithAnyNonWordCharacterRegex = "^\\W.*\\W\$".toRegex(RegexOption.IGNORE_CASE) + return disallowPasswordMessagesForTerms?.joinToString( separator = "|", prefix = "(", postfix = ")" - ) { - val escapedTerm = Regex.escape(it) - "(\\s$escapedTerm\\s|^$escapedTerm\\s|\\s$escapedTerm$|^$escapedTerm$)" + ) { term -> + val escapedTerm = Regex.escape(term) + if (startAndEntWithAnyNonWordCharacterRegex.matches(term)) { + "($escapedTerm)" + } else { + "(\\W$escapedTerm\\W)" + } }?.toRegex(setOf(RegexOption.IGNORE_CASE)) } diff --git a/FlowCrypt/src/test/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfigurationTest.kt b/FlowCrypt/src/test/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfigurationTest.kt index d047c30946..73872bedbc 100644 --- a/FlowCrypt/src/test/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfigurationTest.kt +++ b/FlowCrypt/src/test/java/com/flowcrypt/email/api/retrofit/response/model/ClientConfigurationTest.kt @@ -17,7 +17,7 @@ class ClientConfigurationTest { fun testGetDisallowPasswordMessagesForTermsRegex() { val clientConfiguration = ClientConfiguration( disallowPasswordMessagesForTerms = listOf( - "vendor", + "droid", "[Classification: Data Control: Internal Data Control]", ) ) @@ -28,14 +28,17 @@ class ClientConfigurationTest { "[Classification: Data Control: Internal Data Control] Quarter results", "Conference information [Classification: Data Control: Internal Data Control]", "[Classification: Data Control: Internal Data Control]", - "Conference information [Classification: Data Control: Internal Data Control] Android" + "aaaa[Classification: Data Control: Internal Data Control]bbb", + "[droid]", + "check -droid- case", ) val nonMatchingSubjects = listOf( - "[Classification: Data Control: Internal Data Control]! Quarter results", - "Conference information [Classification: Data Control: Internal Data Control])", - "([Classification: Data Control: Internal Data Control]", - "Conference information ([Classification: Data Control: Internal Data Control]) Android" + "[1Classification: Data Control: Internal Data Control] Quarter results", + "Conference information [1Classification: Data Control: Internal Data Control]", + "[1Classification: Data Control: Internal Data Control]", + "aaaa[1Classification: Data Control: Internal Data Control]bbb", + "Microdroid androids", ) matchingSubjects.forEach { subject -> assertNotNull("Exception in :$subject", regex.find(subject))