test(billing): cover recovery banner permissions - #14713
Conversation
📄 Knowledge reviewDosu skipped reviewing this PR because your organization has used its |
🎭 Playwright: ✅ 1832 passed, 0 failed · 1 flaky📊 Browser Reports
📦 Bundle: 8.86 MB gzip 🟢 -49 BDetailsSummary
Category Glance App Entry Points — 3.71 kB (baseline 3.71 kB) • ⚪ 0 BMain entry bundles and manifests
Status: 1 added / 1 removed Graph Workspace — 1.37 MB (baseline 1.37 MB) • ⚪ 0 BGraph editor runtime, canvas, workflow orchestration
Status: 1 added / 1 removed / 2 unchanged Views & Navigation — 124 kB (baseline 124 kB) • ⚪ 0 BTop-level views, pages, and routed surfaces Status: 17 unchanged Panels & Settings — 566 kB (baseline 566 kB) • ⚪ 0 BConfiguration panels, inspectors, and settings screens Status: 26 unchanged User & Accounts — 27.5 kB (baseline 27.5 kB) • ⚪ 0 BAuthentication, profile, and account management bundles Status: 11 unchanged Editors & Dialogs — 125 kB (baseline 125 kB) • ⚪ 0 BModals, dialogs, drawers, and in-app editors Status: 8 unchanged UI Components — 67.1 kB (baseline 67.1 kB) • ⚪ 0 BReusable component library chunks Status: 14 unchanged Data & Services — 3.53 MB (baseline 3.53 MB) • ⚪ 0 BStores, services, APIs, and repositories Status: 17 unchanged Utilities & Hooks — 549 kB (baseline 549 kB) • ⚪ 0 BHelpers, composables, and utility bundles Status: 37 unchanged Vendor & Third-Party — 16.8 MB (baseline 16.8 MB) • ⚪ 0 BExternal libraries and shared vendor chunks Status: 18 unchanged Other — 14.2 MB (baseline 14.2 MB) • 🟢 -128 BBundles that do not match a named category
Status: 2 added / 2 removed / 284 unchanged ⚡ Performance Report
Show regressions
All metrics
Historical variance (last 15 runs)
Trend (last 15 commits on main)
Raw data{
"timestamp": "2026-08-19T02:02:25.755Z",
"gitSha": "c17af7ee2a3a91a473e9dce6f21a3fd35721b47b",
"branch": "dante01yoon/test-billing-e2e-matrix",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2163.240999999971,
"styleRecalcs": 9,
"styleRecalcDurationMs": 9.086999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 715.505,
"heapDeltaBytes": 7999404,
"heapUsedBytes": 69503880,
"domNodes": -283,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 13.619000000000003,
"eventListeners": -181,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-idle",
"durationMs": 2063.3439999999155,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.019000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 574.167,
"heapDeltaBytes": 10812344,
"heapUsedBytes": 71396076,
"domNodes": -283,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 11.614000000000003,
"eventListeners": -181,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1883.5369999999898,
"styleRecalcs": 74,
"styleRecalcDurationMs": 39.172,
"layouts": 12,
"layoutDurationMs": 3.3310000000000004,
"taskDurationMs": 965.0519999999999,
"heapDeltaBytes": 1087904,
"heapUsedBytes": 61648168,
"domNodes": -282,
"jsHeapTotalBytes": 4448256,
"scriptDurationMs": 124.837,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1910.3090000000975,
"styleRecalcs": 76,
"styleRecalcDurationMs": 41.09,
"layouts": 12,
"layoutDurationMs": 3.778,
"taskDurationMs": 944.9309999999999,
"heapDeltaBytes": 2032392,
"heapUsedBytes": 62717388,
"domNodes": -280,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 119.475,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1690.8039999999573,
"styleRecalcs": 32,
"styleRecalcDurationMs": 19.840999999999998,
"layouts": 6,
"layoutDurationMs": 0.6849999999999998,
"taskDurationMs": 427.5490000000001,
"heapDeltaBytes": 2809560,
"heapUsedBytes": 62970996,
"domNodes": 77,
"jsHeapTotalBytes": 4980736,
"scriptDurationMs": 13.361999999999998,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.670000000000012,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1729.64300000001,
"styleRecalcs": 31,
"styleRecalcDurationMs": 19.261999999999997,
"layouts": 6,
"layoutDurationMs": 0.7509999999999999,
"taskDurationMs": 442.172,
"heapDeltaBytes": 2717176,
"heapUsedBytes": 62884568,
"domNodes": 75,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 14.564,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "dom-widget-clipping",
"durationMs": 596.5260000000399,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.962000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 380.394,
"heapDeltaBytes": 10705012,
"heapUsedBytes": 71227896,
"domNodes": 18,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 55.81499999999999,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 591.2390000000869,
"styleRecalcs": 11,
"styleRecalcDurationMs": 8.242,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 389.47600000000006,
"heapDeltaBytes": 10390748,
"heapUsedBytes": 70754044,
"domNodes": 18,
"jsHeapTotalBytes": 4456448,
"scriptDurationMs": 57.623,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-idle",
"durationMs": 2069.5240000000013,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.038,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 760.807,
"heapDeltaBytes": 674536,
"heapUsedBytes": 75303124,
"domNodes": -268,
"jsHeapTotalBytes": -1314816,
"scriptDurationMs": 18.674000000000003,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-idle",
"durationMs": 2056.3929999999573,
"styleRecalcs": 7,
"styleRecalcDurationMs": 9.090000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 724.8269999999999,
"heapDeltaBytes": 1447068,
"heapUsedBytes": 76385064,
"domNodes": -268,
"jsHeapTotalBytes": -2363392,
"scriptDurationMs": 19.794,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2295.5910000000017,
"styleRecalcs": 69,
"styleRecalcDurationMs": 15.862999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1302.78,
"heapDeltaBytes": 9791204,
"heapUsedBytes": 85629520,
"domNodes": -282,
"jsHeapTotalBytes": 4939776,
"scriptDurationMs": 353.34499999999997,
"eventListeners": -181,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2285.084999999981,
"styleRecalcs": 69,
"styleRecalcDurationMs": 15.723999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1300.388,
"heapDeltaBytes": -6645296,
"heapUsedBytes": 68749992,
"domNodes": -274,
"jsHeapTotalBytes": 221184,
"scriptDurationMs": 351.55699999999996,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3272.4420000000123,
"styleRecalcs": 61,
"styleRecalcDurationMs": 11.163999999999996,
"layouts": 60,
"layoutDurationMs": 7.671,
"taskDurationMs": 1427.31,
"heapDeltaBytes": 15588100,
"heapUsedBytes": 78504700,
"domNodes": 2,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 403.457,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666696,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-zoom",
"durationMs": 3303.2579999999143,
"styleRecalcs": 62,
"styleRecalcDurationMs": 12.324999999999996,
"layouts": 60,
"layoutDurationMs": 7.566000000000001,
"taskDurationMs": 1450.232,
"heapDeltaBytes": 15620040,
"heapUsedBytes": 78400268,
"domNodes": 4,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 402.02700000000004,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "legacy-node-drag",
"durationMs": 2514.2040000000065,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.789000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1547.403,
"heapDeltaBytes": 12525848,
"heapUsedBytes": 95588612,
"domNodes": 14,
"jsHeapTotalBytes": 8650752,
"scriptDurationMs": 494.18499999999995,
"eventListeners": 186,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "legacy-node-drag",
"durationMs": 2713.5729999999967,
"styleRecalcs": 46,
"styleRecalcDurationMs": 10.909000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1614.12,
"heapDeltaBytes": -20267072,
"heapUsedBytes": 62758136,
"domNodes": -247,
"jsHeapTotalBytes": 6881280,
"scriptDurationMs": 504.24,
"eventListeners": 31,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2041.8419999999742,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.486,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 755.36,
"heapDeltaBytes": -12927988,
"heapUsedBytes": 68304728,
"domNodes": -283,
"jsHeapTotalBytes": 3997696,
"scriptDurationMs": 17.56,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "minimap-idle",
"durationMs": 2023.4879999999293,
"styleRecalcs": 4,
"styleRecalcDurationMs": 3.813999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 686.6159999999999,
"heapDeltaBytes": 16844144,
"heapUsedBytes": 77731344,
"domNodes": -270,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 19.208,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 594.0730000000372,
"styleRecalcs": 47,
"styleRecalcDurationMs": 11.744000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 401.15000000000003,
"heapDeltaBytes": 10778168,
"heapUsedBytes": 71439040,
"domNodes": 20,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 118.71600000000001,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 639.4149999999854,
"styleRecalcs": 45,
"styleRecalcDurationMs": 10.226000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 421.24000000000007,
"heapDeltaBytes": 11425424,
"heapUsedBytes": 71998276,
"domNodes": 16,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 123.18799999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2013.1759999999872,
"styleRecalcs": 9,
"styleRecalcDurationMs": 10.769,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 601.979,
"heapDeltaBytes": -3148248,
"heapUsedBytes": 57216788,
"domNodes": -282,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 10.263000000000002,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 2032.4919999999338,
"styleRecalcs": 8,
"styleRecalcDurationMs": 8.176,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 577.379,
"heapDeltaBytes": -874884,
"heapUsedBytes": 59888760,
"domNodes": -282,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 10.193999999999999,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1753.1880000000228,
"styleRecalcs": 75,
"styleRecalcDurationMs": 38.491,
"layouts": 16,
"layoutDurationMs": 4.879,
"taskDurationMs": 863.211,
"heapDeltaBytes": -1092492,
"heapUsedBytes": 59711040,
"domNodes": -281,
"jsHeapTotalBytes": 5758976,
"scriptDurationMs": 94.205,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1744.3680000000086,
"styleRecalcs": 74,
"styleRecalcDurationMs": 37.074999999999996,
"layouts": 16,
"layoutDurationMs": 4.449,
"taskDurationMs": 870.1669999999999,
"heapDeltaBytes": 10971568,
"heapUsedBytes": 71801460,
"domNodes": -281,
"jsHeapTotalBytes": 4710400,
"scriptDurationMs": 97.923,
"eventListeners": -183,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-transition-enter",
"durationMs": 1417.7670000000262,
"styleRecalcs": 19,
"styleRecalcDurationMs": 33.179,
"layouts": 14,
"layoutDurationMs": 15.241999999999999,
"taskDurationMs": 1003.989,
"heapDeltaBytes": -464728,
"heapUsedBytes": 96574440,
"domNodes": 13673,
"jsHeapTotalBytes": 11534336,
"scriptDurationMs": 18.577999999999996,
"eventListeners": 2375,
"totalBlockingTimeMs": 141,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8740.992000000006,
"styleRecalcs": 250,
"styleRecalcDurationMs": 38.169999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4538.209,
"heapDeltaBytes": 8947972,
"heapUsedBytes": 83503252,
"domNodes": -262,
"jsHeapTotalBytes": -1122304,
"scriptDurationMs": 1072.3400000000001,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8493.254999999976,
"styleRecalcs": 250,
"styleRecalcDurationMs": 39.618,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4576.778,
"heapDeltaBytes": -11070556,
"heapUsedBytes": 63660068,
"domNodes": -281,
"jsHeapTotalBytes": 4190208,
"scriptDurationMs": 1097.831,
"eventListeners": -163,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 17935.968,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17090.399999999998,
"heapDeltaBytes": -55252696,
"heapUsedBytes": 165686208,
"domNodes": -8312,
"jsHeapTotalBytes": -7725056,
"scriptDurationMs": 129.36899999999997,
"eventListeners": -16385,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666763,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 17884.233999999993,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 16935.534,
"heapDeltaBytes": -45352880,
"heapUsedBytes": 173939052,
"domNodes": -8312,
"jsHeapTotalBytes": -12259328,
"scriptDurationMs": 124.98700000000001,
"eventListeners": -16389,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.780000000000047,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 21402.56199999999,
"styleRecalcs": 172,
"styleRecalcDurationMs": 21.004999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20761.224,
"heapDeltaBytes": -29283712,
"heapUsedBytes": 190754664,
"domNodes": -8312,
"jsHeapTotalBytes": -11509760,
"scriptDurationMs": 436.51399999999995,
"eventListeners": -16383,
"totalBlockingTimeMs": 124,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 21256.88200000002,
"styleRecalcs": 178,
"styleRecalcDurationMs": 20.754999999999967,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 20708.334,
"heapDeltaBytes": -35039964,
"heapUsedBytes": 184367452,
"domNodes": -8312,
"jsHeapTotalBytes": -12820480,
"scriptDurationMs": 400.68,
"eventListeners": -16383,
"totalBlockingTimeMs": 35,
"frameDurationMs": 18.333333333333332,
"p95FrameDurationMs": 16.80000000000291
},
{
"name": "workflow-execution",
"durationMs": 110.32599999998638,
"styleRecalcs": 7,
"styleRecalcDurationMs": 15.530000000000001,
"layouts": 3,
"layoutDurationMs": 0.7440000000000001,
"taskDurationMs": 83.385,
"heapDeltaBytes": 3162856,
"heapUsedBytes": 63553060,
"domNodes": 114,
"jsHeapTotalBytes": 262144,
"scriptDurationMs": 5.868999999999999,
"eventListeners": 49,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "workflow-execution",
"durationMs": 460.1900000000114,
"styleRecalcs": 13,
"styleRecalcDurationMs": 20.794,
"layouts": 4,
"layoutDurationMs": 1.288,
"taskDurationMs": 108.02199999999999,
"heapDeltaBytes": 4941364,
"heapUsedBytes": 65387048,
"domNodes": 121,
"jsHeapTotalBytes": 0,
"scriptDurationMs": 7.913999999999999,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
}
]
} |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: 📒 Files selected for processing (3)
Included review availability: Your plan provides up to 4 included reviews per hour; 3 remain after this review. 📝 WalkthroughWalkthroughAdded Playwright coverage for owner and member billing recovery states. The changes add billing settings navigation, failed-payment and paused-subscription fixtures, payment-portal assertions, and role-specific visibility checks. ChangesBilling recovery browser coverage
Estimated code review effort: 3 (Moderate) | ~20 minutes Merge Risk: 🔵 Low · up to The PR adds focused billing recovery and privacy coverage, but the new tests still need explicit synchronization for the portal request and broader checks that recovery controls are absent from all relevant content. The change is mergeable with owner awareness of these bounded test-coverage risks. Suggested labels: Important Pre-merge checks failedPlease resolve all errors before merging. Addressing warnings is optional. ❌ Failed checks (1 inconclusive)
✅ Passed checks (6 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@browser_tests/tests/dialogs/billingStatusRecovery.spec.ts`:
- Around line 2-5: Update the billing status recovery test around the click and
portalRequests handling to wait for the payment-portal request before reading
its body. Use Playwright’s wait-before-click pattern, then assert against the
intercepted request body rather than reading portalRequests immediately after
the click.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: 668bc6dd-303c-4a21-8828-d9d67c966f55
📒 Files selected for processing (4)
browser_tests/fixtures/components/WorkspaceBillingSettings.tsbrowser_tests/fixtures/data/cloudWorkspace.tsbrowser_tests/tests/dialogs/billingStatusRecovery.spec.tsbrowser_tests/tests/dialogs/pricingTableDeepLink.spec.ts
| import type { | ||
| PaymentPortalRequest, | ||
| PaymentPortalResponse | ||
| } from '@comfyorg/ingest-types' |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== file list =="
git ls-files | rg '(^|/)billingStatusRecovery\.spec\.ts$|browser_tests/README\.md$|docs/guidance/playwright\.md$|\.agents/checks/playwright-e2e\.md$' || true
echo "== target file outline/size =="
wc -l browser_tests/tests/dialogs/billingStatusRecovery.spec.ts
cat -n browser_tests/tests/dialogs/billingStatusRecovery.spec.ts
echo "== nearby guidance snippets =="
for f in browser_tests/README.md docs/guidance/playwright.md .agents/checks/playwright-e2e.md; do
if [ -f "$f" ]; then
echo "--- $f ---"
rg -n "waitForRequest|page\.waitForRequest|route\(\)|api/billing/payment-portal|assertion|request|Wait" "$f" -C 2 || true
fi
doneRepository: Comfy-Org/ComfyUI_frontend
Length of output: 13518
🌐 Web query:
Playwright page.waitForRequest wait before click pattern
💡 Result:
The recommended pattern for waiting for a request after an action (such as a click) in Playwright is to initiate the wait before performing the action and to use Promise.all to handle both concurrently [1][2][3]. This approach prevents race conditions where the request completes before the test starts listening for it [2][4]. Recommended Pattern Use Promise.all to register the listener and trigger the action simultaneously [2][5]. This ensures Playwright is actively "armed" to intercept the request as soon as it is sent [2]. const [request] = await Promise.all([ page.waitForRequest('/api/target-endpoint'), page.locator('button').click, ]); Why this pattern is necessary If you perform the action first (e.g., await page.click) and then wait (e.g., await page.waitForRequest), there is a risk that the request will be sent and processed by the browser before your test code begins listening for it [2][4]. This leads to intermittent timeout errors, which may appear non-deterministic depending on network speed or browser engine behavior [4][6]. Alternative approach If you do not want to use Promise.all, you can still achieve the same result by creating the promise first without awaiting it, then triggering the action, and finally awaiting the promise [1][7]: // 1. Start waiting for request before the action (no await here) const requestPromise = page.waitForRequest('/api/target-endpoint'); // 2. Perform the action await page.locator('button').click; // 3. Wait for the promise to resolve const request = await requestPromise; Both methods are functionally equivalent, but the Promise.all syntax is often preferred for its readability and conciseness [1][2][5]. Additional Considerations - Predicates: You can use a predicate function instead of a URL string to match requests based on status, headers, or body content (e.g., req.postDataJSON) [5][8][3]. - waitForResponse: In many cases, it is more reliable to wait for the response (page.waitForResponse) rather than the request, as the response confirms that the server has successfully received and processed the action [4]. The same Promise.all pattern applies to waitForResponse [9][5].
Citations:
- 1: [Question] Is Promise.all necessary on waiting for Events? microsoft/playwright#23967
- 2: https://tallybarak.com/the-illustrated-guide-to-using-promise-all-in-playwright-tests-af7a98af3f32
- 3: https://qaskills.sh/blog/playwright-network-interception-route-guide
- 4: https://runebook.dev/en/docs/playwright/api/class-page/page-wait-for-request
- 5: https://getautonoma.com/blog/playwright-api-testing-guide
- 6: [Bug]: waitForRequest is timed out only in Webkit. microsoft/playwright#33318
- 7: https://playwright.dev/docs/events
- 8: https://scrolltest.com/playwright-analytics-event-verification/
- 9: https://playwright.dev/docs/network
Wait for the payment-portal request before reading its body.
Line 67 reads portalRequests immediately after the click, while the route handler is executed asynchronously. Use Playwright’s wait-before-click pattern for the API request and assert from the intercepted request body.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@browser_tests/tests/dialogs/billingStatusRecovery.spec.ts` around lines 2 -
5, Update the billing status recovery test around the click and portalRequests
handling to wait for the payment-portal request before reading its body. Use
Playwright’s wait-before-click pattern, then assert against the intercepted
request body rather than reading portalRequests immediately after the click.
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## main #14713 +/- ##
==========================================
- Coverage 79.18% 78.99% -0.20%
==========================================
Files 2210 1881 -329
Lines 112308 115780 +3472
Branches 34297 33667 -630
==========================================
+ Hits 88930 91459 +2529
- Misses 22912 23792 +880
- Partials 466 529 +63
Flags with carried forward coverage won't be shown. Click here to find out more. 🚀 New features to boost your workflow:
|
Amp-Thread-ID: https://ampcode.com/threads/T-019fce91-ca23-725d-8fa8-e1c8093308aa Co-authored-by: Amp <amp@ampcode.com>
cf3b04d to
38986f2
Compare
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |
|
Resolved the merge conflict by rebasing onto latest New head: Verified: |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@browser_tests/tests/dialogs/billingStatusRecovery.spec.ts`:
- Around line 143-145: Update the recovery-control absence assertion in the
relevant billing recovery test to use the same settings.content-scoped locator
as TB-22B, rather than limiting the search to settings.statusBanner. Keep
asserting that every “Update payment” button within Plan & Credits content has
count zero.
🪄 Autofix
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: b8a94af8-de97-4c2a-b463-68e4461ce146
📒 Files selected for processing (3)
browser_tests/fixtures/components/WorkspaceBillingSettings.tsbrowser_tests/fixtures/data/cloudWorkspace.tsbrowser_tests/tests/dialogs/billingStatusRecovery.spec.ts
| await expect( | ||
| settings.statusBanner.getByRole('button', { name: 'Update payment' }) | ||
| ).toHaveCount(0) |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
Check recovery-control absence in all Plan & Credits content.
The current locator excludes Update payment only inside statusBanner. If the control renders elsewhere in settings.content, this test passes while a member still has recovery access. Use the same content-scoped locator as TB-22B.
Proposed fix
- settings.statusBanner.getByRole('button', { name: 'Update payment' })
+ settings.content.getByRole('button', { name: 'Update payment' })📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| await expect( | |
| settings.statusBanner.getByRole('button', { name: 'Update payment' }) | |
| ).toHaveCount(0) | |
| await expect( | |
| settings.content.getByRole('button', { name: 'Update payment' }) | |
| ).toHaveCount(0) |
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In `@browser_tests/tests/dialogs/billingStatusRecovery.spec.ts` around lines 143 -
145, Update the recovery-control absence assertion in the relevant billing
recovery test to use the same settings.content-scoped locator as TB-22B, rather
than limiting the search to settings.statusBanner. Keep asserting that every
“Update payment” button within Plan & Credits content has count zero.
huang47
left a comment
There was a problem hiding this comment.
One blocking test-reliability issue. Current-head browser evidence: #14713 (comment)
| .getByRole('button', { name: 'Update payment' }) | ||
| .click() | ||
|
|
||
| expect(portalRequests).toEqual([{ return_url: returnUrl }]) |
There was a problem hiding this comment.
Could this intermittently read [] before the /api/billing/payment-portal route callback runs? The click starts a fire-and-forget manageSubscription() flow, so portalRequests is asserted immediately after an async UI mutation. Please poll for one captured request before asserting its payload, matching the existing Playwright pattern.
Current-head browser evidence: #14713 (comment)
## Summary Adds deterministic browser coverage for the current-main partner-node, top-up, and responsive billing-cycle workflows. This PR is stacked on the isolated maximum-warning prerequisite #14815, which is itself stacked on #14713 so the Cloud Team recovery-banner slice remains independently reviewable. ## Why this is needed These journeys span stateful API polling, exact currency payloads, workspace-scoped balances, partner-node recovery, permission boundaries, and responsive pricing interactions. Main and #14713 did not deterministically exercise them, leaving regressions dependent on live billing state and timing. The maximum top-up test also exposed a current frontend bug: manually entering an amount at or above $10,000 clamps the input correctly, but the enterprise guidance was unreachable because the warning only appeared for values strictly greater than the maximum. ## Changes - Adds a typed stateful billing API fixture for deterministic status, balance, plans, preview, top-up, subscription, cancellation, and operation polling responses. - Adds partner-node coverage for price presentation, prompt execution request, sufficient-credit balance deduction/refresh, insufficient-credit recovery after a failed billing refresh, and member privacy/permissions. - Adds top-up coverage for exact-cent personal preset/custom payloads, active Team workspace scoping, pending completion, HTTP failure/retry, failed operation feedback, and the current $10,000/enterprise contract. - Adds monthly-to-yearly Creator pricing interaction through the direct supported pricing route on Cloud desktop and mobile, guarded by nonempty interactive-plan assertions. - Depends on #14815, which isolates the `showCeilingWarning` production prerequisite. ## Root cause and fix ### AS IS `showCeilingWarning` used `value > MAX_AMOUNT`. Because the input is clamped to `MAX_AMOUNT`, the observable value could never remain above the cap, hiding the warning and Contact us link at $10,000. [AS IS screenshot](https://ampcode.com/user-content/artifacts/6f5bcdc4d255307bdaed598273b88977160cebe70cc89f89baa56c5330e79c0f-file.png) ### TO BE The warning uses `value >= MAX_AMOUNT`, preserving the cap while exposing enterprise billing guidance at the reachable maximum. [TO BE screenshot](https://ampcode.com/user-content/artifacts/4ef8548ce8b54dee264330d27bcef202cc5633667fecacd04c46d170a3a4b44b-file.png) ## Review focus - Stateful billing operation polling and mocked balance refresh/deduction behavior. - Team member permission/privacy expectations. - The one-line maximum-warning prerequisite is isolated in #14815. - #14526 is an active draft for a future saved-payment-method UI contract with component tests; these browser tests remain scoped to current-main behavior and do not mock provider metering. - Workspace Activity API wiring and browser coverage are isolated in #14811 and #14813 rather than duplicated here. ## Validation - `pnpm typecheck` and `pnpm typecheck:browser` - Focused Cloud partner-node/top-up/pricing Playwright - Pricing mobile-chrome repeated 3 times and Cloud once after the responsive route fix - ESLint, oxfmt, oxlint, knip, and `git diff --check` ## Stacking - Base: `dante/topup-max-enterprise-warning` (#14815) - #14815 is based on `dante01yoon/test-billing-e2e-matrix` (#14713) - Workspace Activity prerequisite/tests: #14811 → #14813 --------- Co-authored-by: Amp <amp@ampcode.com>
christian-byrne
left a comment
There was a problem hiding this comment.
There are some merge conflicts.
Amp-Thread-ID: https://ampcode.com/threads/T-01a017ad-4c43-767c-b936-c51822ab76e1 Co-authored-by: Amp <amp@ampcode.com> # Conflicts: # browser_tests/fixtures/data/cloudWorkspace.ts
|
Note GitHub couldn't provide a complete incremental comparison for this pull request, so CodeRabbit is performing a full review instead. This review may take a little longer. |

Summary
Restores the current-valid part of historical #13818 that is still missing on
main: Cloud Team billing recovery banners now have browser coverage for owner actions, member privacy, and paused-member guidance. The historical PR remains closed and unchanged.Root cause: #13818 accumulated a broad mocked suite while billing moved to the workspace billing rail. Most of its checkout, top-up, lifecycle, routing, and responsive assertions were later replaced by narrower tests on
main; the remaining payment-recovery tests were stale, untyped against the current ingest contract, and did not verify the portal request or member privacy. This PR rebuilds only that gap and updates shared billing-status fixtures for the now-required seat fields.Changes
WorkspaceBillingSettingspage object and three uniquely traced Cloud scenarios forpayment_failed/pausedTeam subscriptions.return_urland resulting portal navigation.BillingStatusResponseseat-capacity contract.Traceability and current coverage inventory
billingStatusRecovery.spec.ts—exposes payment recovery and sends the current return URLnewbillingStatusRecovery.spec.ts—keeps payment failure details and recovery privatenewbillingStatusRecovery.spec.ts—shows a role-safe pause notice without billing controlsnewlocalCreditsNoSubscribeUi.spec.tssubscriptionPaywallError.spec.ts;billingFacadeConsumers.spec.tspricingTableDeepLink.spec.tsselected personal/Team scenariospricingTableDeepLink.spec.tspending, downgrade, recovered-3DS, and reconciliation scenariostopUpDeepLink.spec.tscreditsTile.spec.ts; conversion expansion remains in #11953endedSubscription.spec.ts;cancelSubscriptionDialog.spec.tsWorkspaceActivityContent.test.tsandUsageLogsTable.test.ts; browser-level event journey is follow-upOrdered follow-up stack
Review Focus
BillingStatusBannerpermission policy.PaymentPortalRequestand asserts the complete payload.pausedis a supported frontend status, but production emission still depends on the backend rollout; this PR validates frontend behavior only.Validation
pnpm typecheckpnpm typecheck:browserpnpm knip --cacheDISTRIBUTION=cloud DISABLE_VUE_PLUGINS=true pnpm dev --host 127.0.0.1PLAYWRIGHT_TEST_URL=http://127.0.0.1:5174 pnpm exec playwright test browser_tests/tests/dialogs/billingStatusRecovery.spec.ts --project=cloud --workers=1— 3 passedpnpm exec playwright test browser_tests/tests/dialogs/billingStatusRecovery.spec.ts --project=cloud --list— 3 uniquely traced tests collectedExternal/manual-only boundaries
paused, provider webhooks, and cross-service status propagation require cloud integration validation.