55 * AC1 – Invalid address error is tested (empty destination blocks submit)
66 * AC2 – Invalid amount error is tested (zero / negative amount blocks submit)
77 * AC3 – Submit is blocked when the form is invalid
8- * AC4 – Valid form calls sendXlmTransaction
9- * AC5 – Failure in sendXlmTransaction displays an error alert
8+ * AC4 – Valid form routes into sign confirmation
9+ * AC5 – Validation includes missing balance / reserve protection states
1010 * AC6 – Scan option exists on the destination field
1111 * AC7 – Camera permission is handled (denied state shown, grant flow works)
1212 * AC8 – A valid scanned address fills the destination field and closes the scanner
@@ -72,11 +72,9 @@ jest.mock('expo-camera', () => ({
7272
7373// ─── Typed mock imports ──────────────────────────────────────────────────
7474
75- import { sendXlmTransaction } from '../src/services/stellar' ;
7675import { useWalletStore } from '../src/store/walletStore' ;
7776import { useRouter } from 'expo-router' ;
7877
79- const mockSendXlmTransaction = sendXlmTransaction as jest . MockedFunction < typeof sendXlmTransaction > ;
8078const mockUseWalletStore = useWalletStore as jest . MockedFunction < typeof useWalletStore > ;
8179const mockUseRouter = useRouter as jest . MockedFunction < typeof useRouter > ;
8280
@@ -120,7 +118,6 @@ beforeEach(() => {
120118 alertSpy . mockImplementation ( ( ) => undefined ) ;
121119 mockUseRouter . mockReturnValue ( { back : mockBack , push : mockPush , replace : mockReplace } as any ) ;
122120 setupWalletStore ( ) ;
123- mockSendXlmTransaction . mockResolvedValue ( { hash : 'abc123' } as any ) ;
124121 mockPermissionGranted = true ;
125122 mockPermissionCanAskAgain = true ;
126123} ) ;
@@ -175,6 +172,19 @@ describe('AC2 – invalid amount error', () => {
175172
176173 expect ( getByText ( "You don't have enough XLM for this payment." ) ) . toBeTruthy ( ) ;
177174 } ) ;
175+
176+ it ( 'shows a reserve-protection error when the payment would leave too little XLM behind' , async ( ) => {
177+ setupWalletStore ( { balance : '5.0000000' } ) ;
178+ const { getByPlaceholderText, getByText } = render ( < SendScreen /> ) ;
179+
180+ fireEvent . changeText ( getByPlaceholderText ( 'G...' ) , VALID_DESTINATION ) ;
181+ fireEvent . changeText ( getByPlaceholderText ( '0.00' ) , '4.5' ) ;
182+ fireEvent . press ( getByText ( 'Send Payment' ) ) ;
183+
184+ expect (
185+ getByText ( 'You need to keep at least 1 XLM in your wallet, so this amount is too high.' ) ,
186+ ) . toBeTruthy ( ) ;
187+ } ) ;
178188} ) ;
179189
180190// ────────────────────────────────────────────────────────────────────────
@@ -200,16 +210,28 @@ describe('AC3 – submit is blocked when the form is invalid', () => {
200210 fireEvent . press ( getByText ( 'Send Payment' ) ) ;
201211
202212 expect ( getByText ( 'Amount must be more than 0.' ) ) . toBeTruthy ( ) ;
203- expect ( mockSendXlmTransaction ) . not . toHaveBeenCalled ( ) ;
213+ expect ( mockPush ) . not . toHaveBeenCalled ( ) ;
214+ } ) ;
215+
216+ it ( 'does not continue when the recipient is the current wallet' , async ( ) => {
217+ setupWalletStore ( { publicKey : VALID_DESTINATION } ) ;
218+ const { getByPlaceholderText, getByText } = render ( < SendScreen /> ) ;
219+
220+ fireEvent . changeText ( getByPlaceholderText ( 'G...' ) , VALID_DESTINATION ) ;
221+ fireEvent . changeText ( getByPlaceholderText ( '0.00' ) , VALID_AMOUNT ) ;
222+ fireEvent . press ( getByText ( 'Send Payment' ) ) ;
223+
224+ expect ( getByText ( "You can't send a payment to your own wallet." ) ) . toBeTruthy ( ) ;
225+ expect ( mockPush ) . not . toHaveBeenCalled ( ) ;
204226 } ) ;
205227} ) ;
206228
207229// ────────────────────────────────────────────────────────────────────────
208- // AC4 – Valid form calls sendXlmTransaction
230+ // AC4 – Valid form routes into sign confirmation
209231// ────────────────────────────────────────────────────────────────────────
210232
211- describe ( 'AC4 – valid form calls router.push to review-transaction ' , ( ) => {
212- it ( 'navigates to review-transaction with correct arguments on a valid submission' , async ( ) => {
233+ describe ( 'AC4 – valid form routes into sign confirmation ' , ( ) => {
234+ it ( 'navigates to sign-confirmation with correct arguments on a valid submission' , async ( ) => {
213235 const { getByPlaceholderText, getByText } = render ( < SendScreen /> ) ;
214236
215237 fireEvent . changeText ( getByPlaceholderText ( 'G...' ) , VALID_DESTINATION ) ;
@@ -218,17 +240,21 @@ describe('AC4 – valid form calls router.push to review-transaction', () => {
218240
219241 await waitFor ( ( ) => {
220242 expect ( mockPush ) . toHaveBeenCalledWith ( {
221- pathname : '/review-transaction ' ,
243+ pathname : '/sign-confirmation ' ,
222244 params : {
245+ source : 'GPUBLIC123' ,
223246 destination : VALID_DESTINATION ,
224247 amount : VALID_AMOUNT ,
248+ assetCode : 'XLM' ,
225249 memo : '' ,
250+ fee : '100' ,
251+ network : 'Testnet' ,
226252 } ,
227253 } ) ;
228254 } ) ;
229255 } ) ;
230256
231- it ( 'passes memo text to review-transaction params when provided' , async ( ) => {
257+ it ( 'passes memo text to sign-confirmation params when provided' , async ( ) => {
232258 const { getByPlaceholderText, getByText } = render ( < SendScreen /> ) ;
233259
234260 fireEvent . changeText ( getByPlaceholderText ( 'G...' ) , VALID_DESTINATION ) ;
@@ -238,11 +264,15 @@ describe('AC4 – valid form calls router.push to review-transaction', () => {
238264
239265 await waitFor ( ( ) => {
240266 expect ( mockPush ) . toHaveBeenCalledWith ( {
241- pathname : '/review-transaction ' ,
267+ pathname : '/sign-confirmation ' ,
242268 params : {
269+ source : 'GPUBLIC123' ,
243270 destination : VALID_DESTINATION ,
244271 amount : VALID_AMOUNT ,
272+ assetCode : 'XLM' ,
245273 memo : 'invoice-42' ,
274+ fee : '100' ,
275+ network : 'Testnet' ,
246276 } ,
247277 } ) ;
248278 } ) ;
@@ -322,7 +352,7 @@ describe('AC7 – camera permission handling', () => {
322352// ────────────────────────────────────────────────────────────────────────
323353
324354describe ( 'AC8 – valid scan fills destination field' , ( ) => {
325- it ( 'closes the scanner and calls sendXlmTransaction with the scanned address on submit' , async ( ) => {
355+ it ( 'closes the scanner and routes into sign confirmation with the scanned address on submit' , async ( ) => {
326356 const { getByLabelText, getByPlaceholderText, getByText, queryByText } = render ( < SendScreen /> ) ;
327357
328358 // We drive this through the same handler the QrScanner would call: onScan.
@@ -339,11 +369,15 @@ describe('AC8 – valid scan fills destination field', () => {
339369
340370 await waitFor ( ( ) => {
341371 expect ( mockPush ) . toHaveBeenCalledWith ( {
342- pathname : '/review-transaction ' ,
372+ pathname : '/sign-confirmation ' ,
343373 params : {
374+ source : 'GPUBLIC123' ,
344375 destination : SCANNED_ADDRESS ,
345376 amount : VALID_AMOUNT ,
377+ assetCode : 'XLM' ,
346378 memo : '' ,
379+ fee : '100' ,
380+ network : 'Testnet' ,
347381 } ,
348382 } ) ;
349383 } ) ;
0 commit comments