fix(cron): preserve chosen clock time across repeat changes

This commit is contained in:
yilsem committed 2026-10-08 02:43:32 +03:00
1 parent 8161f3bf09
commit 1f692044d2
3 files changed
+15 -15

No files matched your search

@@ -1,7 +1,6 @@
package com.m57.hermescontrol.ui.cron package com.m57.hermescontrol.ui.cron
import android.graphics.Bitmap import android.graphics.Bitmap
import android.content.res.Configuration
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
@@ -10,12 +9,10 @@ import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.test.hasContentDescription
import androidx.compose.ui.platform.LocalConfiguration
import androidx.compose.ui.test.junit4.createComposeRule import androidx.compose.ui.test.junit4.createComposeRule
import androidx.compose.ui.test.onAllNodesWithText import androidx.compose.ui.test.onAllNodesWithText
import androidx.compose.ui.test.onNodeWithText import androidx.compose.ui.test.onNodeWithText
@@ -34,13 +31,8 @@ class SchedulePickerTest {
@Test @Test
fun repeatDropdownAndClockPreserveSchedule() { fun repeatDropdownAndClockPreserveSchedule() {
var schedule by mutableStateOf("59 23 * * *") var schedule by mutableStateOf("59 23 * * *")
val baseContext = InstrumentationRegistry.getInstrumentation().targetContext val context = InstrumentationRegistry.getInstrumentation().targetContext
val configuration = Configuration(baseContext.resources.configuration).apply {
setLocale(java.util.Locale.forLanguageTag("tr-TR"))
}
val context = baseContext.createConfigurationContext(configuration)
compose.setContent { compose.setContent {
CompositionLocalProvider(LocalContext provides context, LocalConfiguration provides configuration) {
MaterialTheme { MaterialTheme {
Surface { Surface {
Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(16.dp)) { Column(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(16.dp)) {
@@ -49,10 +41,10 @@ class SchedulePickerTest {
} }
} }
} }
}
fun text(id: Int) = context.getString(id) fun text(id: Int) = context.getString(id)
fun screenshot(name: String) { fun screenshot(name: String) {
compose.waitForIdle() compose.waitForIdle()
android.os.SystemClock.sleep(500)
val bitmap = InstrumentationRegistry.getInstrumentation().uiAutomation.takeScreenshot() val bitmap = InstrumentationRegistry.getInstrumentation().uiAutomation.takeScreenshot()
File(context.getExternalFilesDir(null), "$name.png").outputStream().use { File(context.getExternalFilesDir(null), "$name.png").outputStream().use {
bitmap.compress(Bitmap.CompressFormat.PNG, 100, it) bitmap.compress(Bitmap.CompressFormat.PNG, 100, it)
@@ -62,7 +54,7 @@ class SchedulePickerTest {
compose.onNodeWithText("23:59").performClick() compose.onNodeWithText("23:59").performClick()
screenshot("schedule-clock-360") screenshot("schedule-clock-360")
compose.onNodeWithText("59").performClick() compose.onNodeWithText("59").performClick()
compose.onNodeWithText("55").performClick() compose.onNode(hasContentDescription("55", substring = true)).performClick()
compose.onNodeWithText(text(R.string.action_ok)).performClick() compose.onNodeWithText(text(R.string.action_ok)).performClick()
compose.runOnIdle { assertEquals("55 23 * * *", schedule) } compose.runOnIdle { assertEquals("55 23 * * *", schedule) }
compose.onNodeWithText(text(R.string.cron_repeat_label)).performClick() compose.onNodeWithText(text(R.string.cron_repeat_label)).performClick()
@@ -71,6 +63,12 @@ class SchedulePickerTest {
compose.runOnIdle { assertEquals("55 23 1 1 *", schedule) } compose.runOnIdle { assertEquals("55 23 1 1 *", schedule) }
screenshot("schedule-yearly-360") screenshot("schedule-yearly-360")
compose.onNodeWithText(text(R.string.cron_repeat_label)).performClick() compose.onNodeWithText(text(R.string.cron_repeat_label)).performClick()
compose.onAllNodesWithText(text(R.string.cron_repeat_hourly))[0].performClick()
compose.runOnIdle { assertEquals("55 * * * *", schedule) }
compose.onNodeWithText(text(R.string.cron_repeat_label)).performClick()
compose.onAllNodesWithText(text(R.string.cron_schedule_mode_daily))[0].performClick()
compose.runOnIdle { assertEquals("55 23 * * *", schedule) }
compose.onNodeWithText(text(R.string.cron_repeat_label)).performClick()
compose.onAllNodesWithText(text(R.string.cron_repeat_minutely))[0].performClick() compose.onAllNodesWithText(text(R.string.cron_repeat_minutely))[0].performClick()
compose.runOnIdle { assertEquals("* * * * *", schedule) } compose.runOnIdle { assertEquals("* * * * *", schedule) }
} }
@@ -1004,8 +1004,8 @@ internal fun SchedulePickerSection(
val parsed = remember(schedule) { parseSchedule(schedule) } val parsed = remember(schedule) { parseSchedule(schedule) }
var mode by remember(schedule) { mutableStateOf(parsed.mode) } var mode by remember(schedule) { mutableStateOf(parsed.mode) }
var hour by remember(schedule) { mutableStateOf(parsed.hour) } var hour by remember { mutableStateOf(parsed.hour) }
var minute by remember(schedule) { mutableStateOf(parsed.minute) } var minute by remember { mutableStateOf(parsed.minute) }
var intervalCount by remember(schedule) { mutableStateOf(parsed.intervalCount) } var intervalCount by remember(schedule) { mutableStateOf(parsed.intervalCount) }
var intervalUnit by remember(schedule) { mutableStateOf(parsed.intervalUnit) } var intervalUnit by remember(schedule) { mutableStateOf(parsed.intervalUnit) }
var selectedDays by remember(schedule) { mutableStateOf(parsed.selectedDays) } var selectedDays by remember(schedule) { mutableStateOf(parsed.selectedDays) }
@@ -1055,6 +1055,8 @@ internal fun SchedulePickerSection(
} else { } else {
val fallback = parseSchedule(schedule) val fallback = parseSchedule(schedule)
mode = fallback.mode mode = fallback.mode
hour = fallback.hour
minute = fallback.minute
} }
}, },
) { ) {
@@ -17,7 +17,7 @@ internal data class ParsedSchedule(
val minute: Int = 0, val minute: Int = 0,
val intervalCount: Int = 1, val intervalCount: Int = 1,
val intervalUnit: String = "h", val intervalUnit: String = "h",
val selectedDays: Set<Int> = setOf(1, 2, 3, 4, 5), val selectedDays: Set<Int> = setOf(1),
val dayOfMonth: Int = 1, val dayOfMonth: Int = 1,
val month: Int = 1, val month: Int = 1,
val raw: String = "", val raw: String = "",