mirror of
https://github.com/priyanshujain/sanderling.git
synced 2026-10-02 11:07:10 +00:00
- WebLedgerStore.accountExistsByName case-insensitive (matches SQL COLLATE NOCASE) - drop SampleApplication.maybeInjectDebugError; sample spec now passes clean - drop noLogcatErrors from sample spec (default matches system-wide E logs) - openRandomAccount picks uniformly from findAll instead of first match - clear txnError on any add-transaction interaction, not only valid amount input - escapeForAdbInputText quotes shell metacharacters (quotes, backslash, etc.) - extract buildClearKeyevents helper with empty/normal/cap tests - broaden spec_integration_test.go to cover home, add-account, ledger, add-transaction action generators - document FocusTracker single-focus invariant
This commit is contained in:
8 files changed
+284
-26
No files matched your search
-12
@@ -1,7 +1,6 @@
|
||||
package dev.uatu.sample
|
||||
|
||||
import android.app.Application
|
||||
import android.content.pm.ApplicationInfo
|
||||
import dev.uatu.sdk.Uatu
|
||||
|
||||
class SampleApplication : Application() {
|
||||
@@ -80,16 +79,5 @@ class SampleApplication : Application() {
|
||||
Uatu.extract("login_error") { UiState.loginError.value }
|
||||
Uatu.extract("add_account_error") { UiState.addAccountError.value }
|
||||
Uatu.extract("txn_error") { UiState.txnError.value }
|
||||
maybeInjectDebugError()
|
||||
}
|
||||
|
||||
// Fires a synthetic Uatu.reportError in debug builds so the sample-app
|
||||
// e2e run can verify noUncaughtExceptions surfaces SDK-captured errors
|
||||
// in the trace. Production builds skip this.
|
||||
private fun maybeInjectDebugError() {
|
||||
val isDebuggable = applicationInfo.flags and ApplicationInfo.FLAG_DEBUGGABLE != 0
|
||||
if (isDebuggable) {
|
||||
Uatu.reportError(RuntimeException("synthetic"))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,9 @@ import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
|
||||
// Assumes single-focus: only one input can be focused at a time, so
|
||||
// enter() overwriting is safe. leave() is id-gated so a stale dispose
|
||||
// from a previously-focused field cannot clobber the active focus.
|
||||
object FocusTracker {
|
||||
private val _current = MutableStateFlow<String?>(null)
|
||||
val current: StateFlow<String?> = _current.asStateFlow()
|
||||
|
||||
+9
-3
@@ -103,7 +103,10 @@ fun AddTransactionPage(accountId: String) {
|
||||
Segmented(
|
||||
selected = if (type == TxnType.credit) 0 else 1,
|
||||
labels = listOf("Credit", "Debit"),
|
||||
onSelect = { type = if (it == 0) TxnType.credit else TxnType.debit },
|
||||
onSelect = {
|
||||
UiState.txnError.value = ""
|
||||
type = if (it == 0) TxnType.credit else TxnType.debit
|
||||
},
|
||||
descriptions = listOf("txn_credit", "txn_debit"),
|
||||
)
|
||||
Column(verticalArrangement = Arrangement.spacedBy(6.dp)) {
|
||||
@@ -111,9 +114,9 @@ fun AddTransactionPage(accountId: String) {
|
||||
TextInput(
|
||||
value = amount,
|
||||
onChange = {
|
||||
UiState.txnError.value = ""
|
||||
if (AMOUNT_REGEX.matches(it) || it.isEmpty()) {
|
||||
amount = it
|
||||
UiState.txnError.value = ""
|
||||
}
|
||||
},
|
||||
placeholder = "0.00",
|
||||
@@ -129,7 +132,10 @@ fun AddTransactionPage(accountId: String) {
|
||||
FieldLabel("Note (optional)")
|
||||
TextInput(
|
||||
value = note,
|
||||
onChange = { note = it.take(80) },
|
||||
onChange = {
|
||||
UiState.txnError.value = ""
|
||||
note = it.take(80)
|
||||
},
|
||||
placeholder = "What's this for?",
|
||||
label = "Note",
|
||||
description = "txn_note",
|
||||
|
||||
+1
-1
@@ -21,7 +21,7 @@ class WebLedgerStore : LedgerStore {
|
||||
}
|
||||
|
||||
override fun accountExistsByName(name: String): Boolean =
|
||||
_accounts.value.any { it.name == name }
|
||||
_accounts.value.any { it.name.equals(name, ignoreCase = true) }
|
||||
|
||||
override fun insertAccount(id: String, name: String, createdAt: Long) {
|
||||
_accounts.value = _accounts.value + Account(id, name, createdAt)
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
waitOnce,
|
||||
weighted,
|
||||
} from "@uatu/spec";
|
||||
import { noLogcatErrors, noUncaughtExceptions } from "@uatu/spec/defaults/properties";
|
||||
import { noUncaughtExceptions } from "@uatu/spec/defaults/properties";
|
||||
|
||||
interface AccountSnapshot {
|
||||
id: string;
|
||||
@@ -90,7 +90,9 @@ const txnCredit = extract((state) => state.ax.find("desc:txn_credit"));
|
||||
const txnDebit = extract((state) => state.ax.find("desc:txn_debit"));
|
||||
const txnSubmit = extract((state) => state.ax.find("desc:txn_submit"));
|
||||
const backButton = extract((state) => state.ax.find("desc:Back"));
|
||||
const anyAccountCard = extract((state) => state.ax.find("descPrefix:account_card:"));
|
||||
const allAccountCards = extract((state) =>
|
||||
state.ax.findAll("descPrefix:account_card:"),
|
||||
);
|
||||
|
||||
const accountCountNonNegative = always(() => accountCount.current >= 0);
|
||||
|
||||
@@ -334,8 +336,10 @@ const openAddAccount = actions(() => {
|
||||
|
||||
const openRandomAccount = actions(() => {
|
||||
if (route.current !== "home") return [];
|
||||
const card = anyAccountCard.current;
|
||||
return card ? [Tap({ on: card })] : [];
|
||||
const cards = allAccountCards.current;
|
||||
if (cards.length === 0) return [];
|
||||
const card = cards[Math.floor(Math.random() * cards.length)];
|
||||
return [Tap({ on: card })];
|
||||
});
|
||||
|
||||
const logoutAction = actions(() => {
|
||||
@@ -411,7 +415,6 @@ export const properties = {
|
||||
...stateMachine,
|
||||
...liveness,
|
||||
noUncaughtExceptions,
|
||||
noLogcatErrors,
|
||||
};
|
||||
|
||||
export const actionsRoot = weighted(
|
||||
|
||||
Reference in new issue
Block a user