From 2397a65b9f923c9b5e1d6cfd8e47ebaa9350b35c Mon Sep 17 00:00:00 2001 From: PJ Date: Sun, 31 May 2026 18:07:08 +0530 Subject: [PATCH] feat(folio-spec): wire submitMovesBalanceByTypedAmount property Adds lastAction and totalBalance extractors and uses them in the new property. Drops ledgerRows/ledgerBalance extractors since nothing else referenced them. --- examples/folio/sanderling/spec.ts | 52 +++++++++++++++---------------- 1 file changed, 26 insertions(+), 26 deletions(-) diff --git a/examples/folio/sanderling/spec.ts b/examples/folio/sanderling/spec.ts index a5ef525..300bef4 100644 --- a/examples/folio/sanderling/spec.ts +++ b/examples/folio/sanderling/spec.ts @@ -5,14 +5,12 @@ import { always, extract, from, - keyedBy, next, - now, weighted, whenRoute, } from "@sanderling/spec"; import { defaultActions } from "@sanderling/spec/defaults"; -import { balanceMatchesAddedSum, type LedgerRow } from "./predicates"; +import { submitChangesBalanceByTypedAmount } from "./predicates"; interface Account { name: string; @@ -45,15 +43,20 @@ const accounts = extract("accounts", s => balance: parseDollarCents(card.find({ testTag: "AccountBalance" })?.text), }))); -// Ledger rows: identity composed from the row's stable testTag'd cells. -const ledgerRows = extract("ledgerRows", s => - s.ax.findAll([{ testTag: "LedgerScreen" }, { testTag: "LedgerRow" }]).map(row => ({ - key: keyedBy(row, ["TxnDate", "TxnNote", "TxnAmount"]), - signed: parseDollarCents(row.find({ testTag: "TxnAmount" })?.text), - }))); +// Total balance: sum of AccountCard balances visible on Home plus the +// LedgerBalance shown on Ledger. Both screens read the same DB, so summing +// across whichever screen is visible gives the same authoritative number. +const totalBalance = extract("totalBalance", s => { + const cards = s.ax.findAll([{ testTag: "HomeScreen" }, { testTag: "AccountCard" }]); + const cardSum = cards.reduce( + (sum, c) => sum + parseDollarCents(c.find({ testTag: "AccountBalance" })?.text), + 0, + ); + const ledgerBal = parseDollarCents(s.ax.find({ testTag: "LedgerBalance" })?.text); + return cardSum + ledgerBal; +}); -const ledgerBalance = extract("ledgerBalance", s => - parseDollarCents(s.ax.find({ testTag: "LedgerBalance" })?.text)); +const lastAction = extract("lastAction", s => s.lastAction); const loginEmailField = extract("loginEmailField", s => s.ax.find([{ testTag: "LoginScreen" }, { testTag: "LoginEmail" }])); @@ -89,20 +92,17 @@ const newAccountBalanceIsZero = always( }) ); -// Property 2: when new ledger rows appear, the balance delta equals the SUM -// of the new rows' signed amounts. Single-row and multi-row sums both must -// match; a double-submit lands two rows whose total is twice the balance -// change and trips the sum check. -const balanceMatchesAddedTxn = always( - now(() => route.current === "ledger").implies( - next(() => - balanceMatchesAddedSum( - ledgerRows.previous ?? [], - ledgerRows.current, - ledgerBalance.previous ?? 0, - ledgerBalance.current, - ), - ), +// Property 2: a tap on TxnSubmit must move the total balance by exactly the +// typed amount. A double-submit lands two transactions, so the balance shifts +// by twice the typed amount and the check fires. +const submitMovesBalanceByTypedAmount = always( + next(() => + submitChangesBalanceByTypedAmount({ + lastAction: lastAction.current, + typedAmount: parseDollarCents(txnAmountField.previous?.text), + prevTotalBalance: totalBalance.previous ?? 0, + currTotalBalance: totalBalance.current, + }), ), ); @@ -162,7 +162,7 @@ const addTxn = whenRoute(route, ["home", "ledger", "add-transaction"], () => { export const properties = { newAccountBalanceIsZero, - balanceMatchesAddedTxn, + submitMovesBalanceByTypedAmount, }; export const setup = login;