@@ -504,3 +504,92 @@ test('asset scanner: cat-bearing funding UTXO surfaces the "asset found" warning
504504 const detail = assetRow . locator ( '.utxo-assets-detail' ) ;
505505 await expect ( detail ) . toBeVisible ( ) ;
506506} ) ;
507+
508+ /**
509+ * Regression for the fee-rate picker.
510+ *
511+ * `<app-ordpool-fees-box-clickable>` renders four anchor tiles
512+ * (economy, hour, halfHour, fastest) bound to the values from
513+ * StateService.recommendedFees$. Clicking a tile emits
514+ * `feeClicked.emit(<rate>)`, which the parent component receives in
515+ * `setFeeRate($event)` and forwards into the `cfeeRate` FormControl.
516+ * The number input bound to `[formControl]="cfeeRate"` updates
517+ * accordingly.
518+ *
519+ * The stub answers the mempool WebSocket on connect with a snapshot
520+ * containing `{fees:{fastestFee:5, halfHourFee:3, hourFee:1,
521+ * economyFee:1, minimumFee:1}}`. So this test pins:
522+ *
523+ * 1. The picker renders the four numeric rates straight from the
524+ * WebSocket-derived `recommendedFees$` stream (proves WS → state
525+ * → template flow).
526+ * 2. Clicking the fastest tile writes `5` into the manual fee-rate
527+ * input.
528+ * 3. Clicking the economy tile writes `1` (or, on this stub,
529+ * identical to hour because both are 1).
530+ * 4. The picker survives a page reload — the WS reconnect populates
531+ * fees again without needing a user action.
532+ */
533+ test ( 'fee picker: tier clicks update the manual fee-rate input' , async ( ) => {
534+ test . setTimeout ( 120_000 ) ;
535+
536+ const page = await context . newPage ( ) ;
537+ await page . goto ( `${ FRONTEND_URL } ${ MINT_PATH } ` , { waitUntil : 'domcontentloaded' } ) ;
538+ await shot ( page , 'fp-01-loaded' ) ;
539+
540+ // Auto-reconnect from localStorage — same dance as the asset-scanner
541+ // test. The wallet must be connected for the form (and the picker)
542+ // to render.
543+ const known = new Set ( context . pages ( ) ) ;
544+ const reapprove = await waitForApprovalPopup ( {
545+ context,
546+ knownPages : known ,
547+ timeoutMs : 6_000 ,
548+ isApproval : async ( p ) => p . url ( ) . startsWith ( 'chrome-extension://' ) ,
549+ } ) . catch ( ( ) => null ) ;
550+ if ( reapprove ) {
551+ await reapprove . getByRole ( 'button' , { name : / ^ ( c o n n e c t | a p p r o v e | c o n f i r m | a l l o w ) $ / i } )
552+ . first ( ) . click ( ) . catch ( ( ) => undefined ) ;
553+ await reapprove . close ( ) . catch ( ( ) => undefined ) ;
554+ }
555+
556+ // Wait for the picker to leave its skeleton-loading template.
557+ // While loading, the four `.item` divs sit inside
558+ // `.loading-container` and contain `.skeleton-loader` rather than
559+ // the real `<a>` tiles. Anchor presence is the cheapest signal.
560+ const tiles = page . locator ( '.fee-estimation-container .item a' ) ;
561+ await expect ( tiles ) . toHaveCount ( 4 , { timeout : 45_000 } ) ;
562+ await shot ( page , 'fp-02-picker-ready' ) ;
563+
564+ // The tile order is fixed: 0=economy, 1=hour, 2=halfHour, 3=fastest.
565+ // Stub fees: economy=1, hour=1, halfHour=3, fastest=5. Each tile
566+ // renders `<app-fee-rate>` showing "<rate> sat/vB". We pin each
567+ // tile's text contains both the expected rate and the unit so
568+ // we don't accidentally match a substring like "15" against "1".
569+ await expect ( tiles . nth ( 2 ) ) . toContainText ( '3' , { timeout : 10_000 } ) ;
570+ await expect ( tiles . nth ( 2 ) ) . toContainText ( 'sat/vB' ) ;
571+ await expect ( tiles . nth ( 3 ) ) . toContainText ( '5' ) ;
572+ await expect ( tiles . nth ( 3 ) ) . toContainText ( 'sat/vB' ) ;
573+
574+ // The manual fee-rate input the mint form binds via
575+ // `[formControl]="cfeeRate"`. Pin it by the surrounding input-group
576+ // label (same selector test 1 uses).
577+ const feeRateInput = page . locator (
578+ '.input-group:has(.input-group-text:text-is("Fee rate")) input[type="number"]' ,
579+ ) . first ( ) ;
580+ await expect ( feeRateInput ) . toBeVisible ( ) ;
581+
582+ // Click the fastest tile (index 3) — expect the input to read "5".
583+ await tiles . nth ( 3 ) . click ( ) ;
584+ await expect ( feeRateInput ) . toHaveValue ( '5' , { timeout : 5_000 } ) ;
585+ await shot ( page , 'fp-03-fastest-clicked' ) ;
586+
587+ // Click the halfHour tile (index 2) — expect input "3".
588+ await tiles . nth ( 2 ) . click ( ) ;
589+ await expect ( feeRateInput ) . toHaveValue ( '3' , { timeout : 5_000 } ) ;
590+ await shot ( page , 'fp-04-halfhour-clicked' ) ;
591+
592+ // Click the hour tile (index 1) — expect input "1".
593+ await tiles . nth ( 1 ) . click ( ) ;
594+ await expect ( feeRateInput ) . toHaveValue ( '1' , { timeout : 5_000 } ) ;
595+ } ) ;
0 commit comments