@@ -189,6 +189,21 @@ describe('OrderDetails', () => {
189189 expect ( mockRefreshOrder ) . toHaveBeenCalled ( ) ;
190190 } ) ;
191191
192+ it ( 'shows localized error when pending order refresh rejects with non-Error' , async ( ) => {
193+ mockUseParams . mockReturnValue ( { orderId : 'ord-pending' } ) ;
194+ mockGetOrderById . mockReturnValue ( {
195+ ...mockOrder ,
196+ status : RampsOrderStatus . Pending ,
197+ } ) ;
198+ mockRefreshOrder . mockRejectedValue ( 'not-an-error' ) ;
199+
200+ const { getByText } = render ( ) ;
201+
202+ await waitFor ( ( ) => {
203+ expect ( getByText ( 'ramps_order_details.error_message' ) ) . toBeOnTheScreen ( ) ;
204+ } ) ;
205+ } ) ;
206+
192207 it ( 'tracks RAMPS_SCREEN_VIEWED when order is displayed' , async ( ) => {
193208 render ( ) ;
194209 await waitFor ( ( ) => {
@@ -256,6 +271,126 @@ describe('OrderDetails', () => {
256271 } ) ;
257272 } ) ;
258273
274+ it ( 'resets to build quote when callback returns no order' , async ( ) => {
275+ mockUseParams . mockReturnValue ( {
276+ callbackUrl : 'https://callback.example?x=1' ,
277+ providerCode : 'moonpay' ,
278+ walletAddress : '0x123' ,
279+ } ) ;
280+ mockGetOrderById . mockReturnValue ( undefined ) ;
281+ mockGetOrderFromCallback . mockResolvedValue ( null ) ;
282+
283+ render ( ) ;
284+
285+ await waitFor ( ( ) => {
286+ expect ( mockReset ) . toHaveBeenCalled ( ) ;
287+ } ) ;
288+ const resetArg = mockReset . mock . calls [ 0 ] [ 0 ] as {
289+ routes : { name : string } [ ] ;
290+ } ;
291+ expect ( resetArg . routes [ 0 ] . name ) . toBe ( Routes . RAMP . BUILD_QUOTE ) ;
292+ } ) ;
293+
294+ it ( 'resets to build quote when callback order is in a bailed status' , async ( ) => {
295+ mockUseParams . mockReturnValue ( {
296+ callbackUrl : 'https://callback.example?x=1' ,
297+ providerCode : 'moonpay' ,
298+ walletAddress : '0x123' ,
299+ } ) ;
300+ mockGetOrderById . mockReturnValue ( undefined ) ;
301+ mockGetOrderFromCallback . mockResolvedValue ( {
302+ providerOrderId : 'ord-bail' ,
303+ status : RampsOrderStatus . Precreated ,
304+ provider : { id : 'moonpay' } ,
305+ walletAddress : '0x123' ,
306+ } ) ;
307+
308+ render ( ) ;
309+
310+ await waitFor ( ( ) => {
311+ expect ( mockReset ) . toHaveBeenCalled ( ) ;
312+ } ) ;
313+ expect ( mockAddOrder ) . not . toHaveBeenCalled ( ) ;
314+ } ) ;
315+
316+ it ( 'uses route cryptocurrency for toast when callback order has no crypto symbol' , async ( ) => {
317+ const { showV2OrderToast } = jest . requireMock (
318+ '../../utils/v2OrderToast' ,
319+ ) as { showV2OrderToast : jest . Mock } ;
320+ const orderWithoutCryptoSymbol = {
321+ providerOrderId : 'ord-cb-2' ,
322+ status : RampsOrderStatus . Completed ,
323+ cryptoAmount : '1' ,
324+ provider : { id : 'moonpay' } ,
325+ walletAddress : '0x123' ,
326+ } ;
327+ mockUseParams . mockReturnValue ( {
328+ callbackUrl : 'https://callback.example?x=1' ,
329+ providerCode : 'moonpay' ,
330+ walletAddress : '0x123' ,
331+ cryptocurrency : 'SOL' ,
332+ } ) ;
333+ mockGetOrderById . mockReturnValue ( undefined ) ;
334+ mockGetOrderFromCallback . mockResolvedValue ( orderWithoutCryptoSymbol ) ;
335+
336+ render ( ) ;
337+
338+ await waitFor ( ( ) => {
339+ expect ( showV2OrderToast ) . toHaveBeenCalledWith (
340+ expect . objectContaining ( {
341+ orderId : 'ord-cb-2' ,
342+ cryptocurrency : 'SOL' ,
343+ } ) ,
344+ ) ;
345+ } ) ;
346+ } ) ;
347+
348+ it ( 'does not emit status metrics when callback order status matches prior order' , async ( ) => {
349+ const { showV2OrderToast } = jest . requireMock (
350+ '../../utils/v2OrderToast' ,
351+ ) as { showV2OrderToast : jest . Mock } ;
352+ const completedOrder = {
353+ providerOrderId : 'ord-same' ,
354+ status : RampsOrderStatus . Completed ,
355+ cryptoCurrency : { symbol : 'ETH' } ,
356+ cryptoAmount : '0.1' ,
357+ provider : { id : 'moonpay' } ,
358+ walletAddress : '0x123' ,
359+ } ;
360+ mockUseParams . mockReturnValue ( {
361+ callbackUrl : 'https://callback.example?x=1' ,
362+ providerCode : 'moonpay' ,
363+ walletAddress : '0x123' ,
364+ } ) ;
365+ mockGetOrderById . mockImplementation ( ( id : string ) =>
366+ id === 'ord-same' ? completedOrder : undefined ,
367+ ) ;
368+ mockGetOrderFromCallback . mockResolvedValue ( completedOrder ) ;
369+
370+ render ( ) ;
371+
372+ await waitFor ( ( ) => {
373+ expect ( showV2OrderToast ) . toHaveBeenCalled ( ) ;
374+ } ) ;
375+ expect ( mockHandleOrderStatusChangedForMetrics ) . not . toHaveBeenCalled ( ) ;
376+ } ) ;
377+
378+ it ( 'shows localized error when callback fetch rejects with Error that has no message' , async ( ) => {
379+ mockUseParams . mockReturnValue ( {
380+ callbackUrl : 'https://callback.example?x=1' ,
381+ providerCode : 'moonpay' ,
382+ walletAddress : '0x123' ,
383+ } ) ;
384+ mockGetOrderById . mockReturnValue ( undefined ) ;
385+ mockGetOrderFromCallback . mockRejectedValue ( new Error ( '' ) ) ;
386+
387+ const { getByText } = render ( ) ;
388+
389+ await waitFor ( ( ) => {
390+ expect ( getByText ( 'ramps_order_details.error_message' ) ) . toBeOnTheScreen ( ) ;
391+ } ) ;
392+ } ) ;
393+
259394 it ( 'shows error state with retry when initial callback fetch fails' , async ( ) => {
260395 mockUseParams . mockReturnValue ( {
261396 callbackUrl : 'metamask://on-ramp/providers/paypal?orderId=abc' ,
0 commit comments