Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package org.mozilla.tryfox.ui.composables

import androidx.compose.ui.test.assertIsDisplayed
import androidx.compose.ui.test.assertIsSelected
import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithTag
import androidx.compose.ui.test.onNodeWithTag
import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performClick
import androidx.test.ext.junit.runners.AndroidJUnit4
import kotlinx.datetime.LocalDate
import org.junit.Assert.assertTrue
Expand All @@ -14,7 +17,10 @@ import org.mozilla.tryfox.data.DownloadState
import org.mozilla.tryfox.ui.models.AbiUiModel
import org.mozilla.tryfox.ui.models.ApkUiModel
import org.mozilla.tryfox.ui.theme.TryFoxTheme
import org.mozilla.tryfox.util.FENIX_BETA
import org.mozilla.tryfox.util.FENIX_RELEASE
import org.mozilla.tryfox.util.FOCUS
import org.mozilla.tryfox.util.FOCUS_BETA
import org.mozilla.tryfox.util.FOCUS_RELEASE
import java.io.File

Expand Down Expand Up @@ -113,6 +119,139 @@ class ArchiveGroupCardHeaderLayoutTest {
)
}

@Test
fun releaseVersionSelector_opensAtSelectedMajorAndConfirmsChosenVariant() {
var confirmedVersion: String? = null
composeTestRule.setContent {
TryFoxTheme(dynamicColor = false) {
ArchiveGroupCard(
apks = listOf(createApkUiModel(FOCUS_RELEASE, "151.0.1", "")),
onDownloadClick = {}, onInstallClick = {}, onOpenAppClick = {}, onUninstallClick = {},
appState = null, onDateSelected = {}, userPickedDate = null,
selectedReleaseVersion = "151.0.1",
availableReleaseVersions = listOf("151.0.1", "151.0.0", "150.0.1"),
appName = FOCUS_RELEASE, errorMessage = null, isLoading = false,
dateValidator = { true }, onClearDate = {},
onReleaseVersionSelected = { confirmedVersion = it },
)
}
}

composeTestRule.onNodeWithTag("release_version_chip_focus-release", useUnmergedTree = true).performClick()
composeTestRule.onNodeWithTag("release_version_selector_sheet_focus-release", useUnmergedTree = true).assertIsDisplayed()
composeTestRule.onNodeWithText("Version").assertIsDisplayed()
composeTestRule.onNodeWithText("Builds:").assertIsDisplayed()
composeTestRule.onNodeWithTag("release_version_variant_151_0_1", useUnmergedTree = true).assertIsSelected()

composeTestRule.onNodeWithTag("release_version_major_picker_focus-release", useUnmergedTree = true).assertIsDisplayed()
composeTestRule.onNodeWithTag("release_version_variant_151_0_0", useUnmergedTree = true).performClick()

assertTrue(confirmedVersion == "151.0.0")
assertTrue(
composeTestRule
.onAllNodesWithTag("release_version_selector_sheet_focus-release", useUnmergedTree = true)
.fetchSemanticsNodes()
.isEmpty(),
)
}

@Test
fun betaVersionSelector_filtersVariantsAndCancelKeepsSelection() {
var confirmedVersion: String? = null
composeTestRule.setContent {
TryFoxTheme(dynamicColor = false) {
ArchiveGroupCard(
apks = listOf(createApkUiModel(FOCUS_BETA, "151.0b2", "")),
onDownloadClick = {}, onInstallClick = {}, onOpenAppClick = {}, onUninstallClick = {},
appState = null, onDateSelected = {}, userPickedDate = null,
selectedReleaseVersion = "151.0b2",
availableReleaseVersions = listOf("151.0b2", "151.0b1", "150.0b3"),
appName = FOCUS_BETA, errorMessage = null, isLoading = false,
dateValidator = { true }, onClearDate = {},
onReleaseVersionSelected = { confirmedVersion = it },
)
}
}

composeTestRule.onNodeWithTag("release_version_chip_focus-beta", useUnmergedTree = true).performClick()
composeTestRule.onNodeWithTag("release_version_variant_151_0b2", useUnmergedTree = true).assertIsSelected()
assertTrue(
composeTestRule
.onAllNodesWithTag("release_version_variant_150_0b3", useUnmergedTree = true)
.fetchSemanticsNodes()
.isEmpty(),
)

composeTestRule.onNodeWithTag("release_version_major_picker_focus-beta", useUnmergedTree = true).assertIsDisplayed()
composeTestRule.onNodeWithText("Cancel").performClick()

assertTrue(confirmedVersion == null)
}

@Test
fun versionSelector_supportsTheHighestMajorProvidedByTheArchive() {
composeTestRule.setContent {
TryFoxTheme(dynamicColor = false) {
ArchiveGroupCard(
apks = listOf(createApkUiModel(FOCUS_RELEASE, "155.0.1", "")),
onDownloadClick = {}, onInstallClick = {}, onOpenAppClick = {}, onUninstallClick = {},
appState = null, onDateSelected = {}, userPickedDate = null,
selectedReleaseVersion = "155.0.1",
availableReleaseVersions = listOf("155.0.1", "154.0.2"),
appName = FOCUS_RELEASE, errorMessage = null, isLoading = false,
dateValidator = { true }, onClearDate = {}, onReleaseVersionSelected = {},
)
}
}

composeTestRule.onNodeWithTag("release_version_chip_focus-release", useUnmergedTree = true).performClick()
composeTestRule.onNodeWithTag("release_version_variant_155_0_1", useUnmergedTree = true).assertIsDisplayed()
}

@Test
fun fenixReleaseSelector_showsCandidateVariantsAndConfirmsTheSelectedRc() {
var confirmedVersion: String? = null
composeTestRule.setContent {
TryFoxTheme(dynamicColor = false) {
ArchiveGroupCard(
apks = listOf(createApkUiModel(FENIX_RELEASE, "153.0.4", "")),
onDownloadClick = {}, onInstallClick = {}, onOpenAppClick = {}, onUninstallClick = {},
appState = null, onDateSelected = {}, userPickedDate = null,
selectedReleaseVersion = "153.0.4",
availableReleaseVersions = listOf("153.0.4", "153.0.4-RC2", "153.0.4-RC1"),
appName = FENIX_RELEASE, errorMessage = null, isLoading = false,
dateValidator = { true }, onClearDate = {},
onReleaseVersionSelected = { confirmedVersion = it },
)
}
}

composeTestRule.onNodeWithTag("release_version_chip_fenix-release", useUnmergedTree = true).performClick()
composeTestRule.onNodeWithTag("release_version_variant_153_0_4-RC2", useUnmergedTree = true).performClick()

assertTrue(confirmedVersion == "153.0.4-RC2")
}

@Test
fun fenixBetaSelector_showsCandidateVariants() {
composeTestRule.setContent {
TryFoxTheme(dynamicColor = false) {
ArchiveGroupCard(
apks = listOf(createApkUiModel(FENIX_BETA, "153.0b5", "")),
onDownloadClick = {}, onInstallClick = {}, onOpenAppClick = {}, onUninstallClick = {},
appState = null, onDateSelected = {}, userPickedDate = null,
selectedReleaseVersion = "153.0b5",
availableReleaseVersions = listOf("153.0b5", "153.0b5-RC2", "153.0b5-RC1"),
appName = FENIX_BETA, errorMessage = null, isLoading = false,
dateValidator = { true }, onClearDate = {}, onReleaseVersionSelected = {},
)
}
}

composeTestRule.onNodeWithTag("release_version_chip_fenix-beta", useUnmergedTree = true).performClick()
composeTestRule.onNodeWithTag("release_version_variant_153_0b5-RC2", useUnmergedTree = true).assertIsDisplayed()
}

private fun createApkUiModel(
appName: String,
version: String,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import org.mozilla.tryfox.util.FENIX_BETA
import org.mozilla.tryfox.util.FENIX_DEBUG
import org.mozilla.tryfox.util.FENIX_DEBUG_PACKAGE
import org.mozilla.tryfox.util.FENIX_RELEASE
import org.mozilla.tryfox.util.FOCUS_RELEASE
import java.io.File

@RunWith(AndroidJUnit4::class)
Expand Down Expand Up @@ -207,4 +208,60 @@ class HomeAppCardTest {

assertEquals(buildDate, selectedDate)
}

@Test
fun releaseVersionOnHomeCard_opensVersionSelectorSheet() {
val version = "151.0.1"
composeTestRule.setContent {
TryFoxTheme {
HomeAppCard(
card = HomeAppCardUiModel(
family = HomeAppFamily.Focus,
selectedAppName = FOCUS_RELEASE,
appsByName = mapOf(
FOCUS_RELEASE to AppUiModel(
name = FOCUS_RELEASE,
packageName = "org.mozilla.focus",
installedVersion = null,
installedDate = null,
selectedReleaseVersion = version,
availableReleaseVersions = listOf(version, "151.0.0", "150.0.1"),
apks = ApksResult.Success(
listOf(
ApkUiModel(
originalString = "",
date = "",
appName = FOCUS_RELEASE,
version = version,
abi = AbiUiModel("arm64-v8a", true),
url = "https://example.invalid/focus.apk",
fileName = "focus.apk",
uniqueKey = "focus-release/focus.apk",
apkDir = File("/tmp/focus"),
),
),
),
),
),
),
installStates = emptyMap(),
onFlavorSelected = {},
onDownloadClick = {},
onInstallClick = {},
onOpenInstalledApp = {},
onOpenTryBuild = { _, _ -> },
onDateSelected = { _, _ -> },
dateValidator = { true },
onReleaseVersionSelected = { _, _ -> },
onBuildSelected = { _, _ -> },
onDismissBuildPicker = {},
)
}
}

composeTestRule.onNodeWithTag("release_version_chip_focus-release", useUnmergedTree = true).performClick()
composeTestRule
.onNodeWithTag("release_version_selector_sheet_focus-release", useUnmergedTree = true)
.assertIsDisplayed()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,35 @@ class MozillaArchiveHtmlParser {
}
}

/** Returns candidate base versions, without their `-candidates` directory suffix. */
fun parseFenixCandidateVersionsFromHtml(html: String, releaseType: ReleaseType): List<String> {
val directoryPattern = Regex("<a href=\"[^\"]+\">([^<]+/)</a>")
return directoryPattern.findAll(html)
.mapNotNull { it.groups[1]?.value?.removeSuffix("/") }
.filter { it.endsWith("-candidates") }
.map { it.removeSuffix("-candidates") }
.filter { candidate ->
when (releaseType) {
ReleaseType.Beta -> candidate.matches(Regex("\\d+\\.\\d+b\\d+"))
ReleaseType.Release -> isStableReleaseVersion(candidate)
}
}
.distinct()
.sortedWith(::compareReleaseVersions)
.toList()
.reversed()
}

/** Extracts numeric build directories such as `build1/` and `build12/`. */
fun parseCandidateBuildNumbersFromHtml(html: String): List<Int> {
val directoryPattern = Regex("<a href=\"[^\"]+\">build(\\d+)/</a>")
return directoryPattern.findAll(html)
.mapNotNull { it.groups[1]?.value?.toIntOrNull() }
.distinct()
.sortedDescending()
.toList()
}

fun parseFenixReleaseAbisFromHtml(html: String, appName: String): List<String> {
// Pattern: {appName}-D+.D+(.D+)?-android-ABI/ or {appName}-D+.D+(.D+)?-android/
// Also supports beta/alpha markers: {appName}-D+.D+(.D+)?[ab]D+-android-ABI/
Expand Down Expand Up @@ -87,6 +116,19 @@ class MozillaArchiveHtmlParser {
}

internal fun compareReleaseVersions(version1: String, version2: String): Int {
val rc1 = parseCandidateDisplayVersion(version1)
val rc2 = parseCandidateDisplayVersion(version2)
if (rc1 != null || rc2 != null) {
val base1 = rc1?.first ?: version1
val base2 = rc2?.first ?: version2
val baseComparison = compareReleaseVersionsWithoutRc(base1, base2)
if (baseComparison != 0) return baseComparison
return (rc1?.second ?: Int.MAX_VALUE).compareTo(rc2?.second ?: Int.MAX_VALUE)
}
return compareReleaseVersionsWithoutRc(version1, version2)
}

private fun compareReleaseVersionsWithoutRc(version1: String, version2: String): Int {
val parts1 = version1.split(Regex("[.b-]")).mapNotNull { it.toIntOrNull() }
val parts2 = version2.split(Regex("[.b-]")).mapNotNull { it.toIntOrNull() }

Expand All @@ -101,6 +143,11 @@ class MozillaArchiveHtmlParser {
return 0
}

private fun parseCandidateDisplayVersion(version: String): Pair<String, Int>? {
val match = Regex("^(.+)-RC(\\d+)$").matchEntire(version) ?: return null
return match.groupValues[1] to match.groupValues[2].toInt()
}

private fun isStableReleaseVersion(version: String): Boolean {
val isPreRelease = version.contains(Regex("[ab]\\d+|beta|alpha|rc", RegexOption.IGNORE_CASE))
return !isPreRelease && version.matches(Regex("\\d+\\.\\d+(\\.\\d+)?"))
Expand Down
Loading
Loading