1- using System . Diagnostics ;
2-
31using BTCPayServer . Plugins . Monero . Services ;
42using BTCPayServer . Rating ;
53using BTCPayServer . Services . Rates ;
@@ -13,6 +11,87 @@ namespace BTCPayServer.Plugins.IntegrationTests.Monero;
1311
1412public class MoneroPluginIntegrationTest ( ITestOutputHelper helper ) : MoneroAndBitcoinIntegrationTestBase ( helper )
1513{
14+ #region Methods
15+
16+ private async Task CreateWalletViaForm (
17+ PlaywrightTester s ,
18+ string walletName ,
19+ string address ,
20+ string viewKey ,
21+ string password ,
22+ string restoreHeight )
23+ {
24+ await s . Page . FillAsync ( "input#WalletName" , walletName ) ;
25+ await s . Page . FillAsync ( "input#PrimaryAddress" , address ) ;
26+ await s . Page . FillAsync ( "input#PrivateViewKey" , viewKey ) ;
27+ await s . Page . FillAsync ( "input#WalletPassword" , password ) ;
28+ await s . Page . FillAsync ( "input#RestoreHeight" , restoreHeight ) ;
29+ await s . Page . ClickAsync ( "button[name='command'][value='connect-wallet']" ) ;
30+ await s . Page . Locator ( $ ".wallet-card[data-wallet='{ walletName } ']") . WaitForAsync ( ) ;
31+ }
32+
33+ private async Task CreateWalletViaModal (
34+ PlaywrightTester s ,
35+ string walletName ,
36+ string address ,
37+ string viewKey ,
38+ string password ,
39+ string restoreHeight )
40+ {
41+ await s . Page . FillAsync ( "#createWalletModal input#WalletName" , walletName ) ;
42+ await s . Page . FillAsync ( "#createWalletModal input#PrimaryAddress" , address ) ;
43+ await s . Page . FillAsync ( "#createWalletModal input#PrivateViewKey" , viewKey ) ;
44+ await s . Page . FillAsync ( "#createWalletModal input#WalletPassword" , password ) ;
45+ await s . Page . FillAsync ( "#createWalletModal input#RestoreHeight" , restoreHeight ) ;
46+ await s . Page . ClickAsync ( "#createWalletModal button[name='command'][value='replace-wallet']" ) ;
47+ await s . Page . Locator ( $ ".wallet-card[data-wallet='{ walletName } ']") . WaitForAsync ( ) ;
48+
49+ }
50+
51+ private async Task < bool > WalletExists ( PlaywrightTester s , string walletName )
52+ {
53+ var walletCard = s . Page . Locator ( $ ".wallet-card[data-wallet='{ walletName } ']") ;
54+ try
55+ {
56+ await walletCard . WaitForAsync ( ) ;
57+ return true ;
58+ }
59+ catch
60+ {
61+ return false ;
62+ }
63+ }
64+
65+ private async Task SwitchActiveWallet ( PlaywrightTester s , string walletName , string password )
66+ {
67+ var walletCard = s . Page . Locator ( $ ".wallet-card[data-wallet='{ walletName } ']") ;
68+ await walletCard . Locator ( ".wallet-card-content" ) . ClickAsync ( ) ;
69+ await s . Page . Locator ( "input#ActiveWalletPassword" ) . WaitForAsync ( ) ;
70+ await s . Page . FillAsync ( "input#ActiveWalletPassword" , password ) ;
71+ await s . Page . ClickAsync ( "button[name='command'][value='set-active-wallet']" ) ;
72+ await s . Page . Locator ( $ ".wallet-card[data-wallet='{ walletName } '].wallet-card-active") . WaitForAsync ( ) ;
73+ }
74+
75+ private async Task DeleteWallet ( PlaywrightTester s , string walletName )
76+ {
77+ var walletCard = s . Page . Locator ( $ ".wallet-card[data-wallet='{ walletName } ']") ;
78+ await walletCard . Locator ( ".wallet-actions .dropdown-toggle" ) . ClickAsync ( ) ;
79+ await walletCard . Locator ( ".wallet-action-delete" ) . WaitForAsync ( ) ;
80+ await walletCard . Locator ( ".wallet-action-delete" ) . ClickAsync ( ) ;
81+ await s . Page . Locator ( "#deleteWalletModal" ) . WaitForAsync ( ) ;
82+ await s . Page . ClickAsync ( "#deleteWalletModal button[name='command'][value='delete-wallets']" ) ;
83+ await walletCard . WaitForAsync ( new ( ) { State = Microsoft . Playwright . WaitForSelectorState . Detached } ) ;
84+ }
85+
86+ private async Task < bool > WalletIsActive ( PlaywrightTester s , string walletName )
87+ {
88+ var walletCard = s . Page . Locator ( $ ".wallet-card[data-wallet='{ walletName } ']") ;
89+ var hasActiveClass = await walletCard . GetAttributeAsync ( "class" ) ;
90+ return hasActiveClass ? . Contains ( "wallet-card-active" ) ?? false ;
91+ }
92+
93+ #endregion
94+
1695 [ Fact ]
1796 public async Task EnableMoneroPluginSuccessfully ( )
1897 {
@@ -40,73 +119,74 @@ public async Task EnableMoneroPluginSuccessfully()
40119 await s . RegisterNewUser ( true ) ;
41120 await s . CreateNewStore ( preferredExchange : "Kraken" ) ;
42121 await s . Page . Locator ( "a.nav-link[href*='monerolike/XMR']" ) . ClickAsync ( ) ;
43- await s . Page . Locator ( "input#PrimaryAddress" )
44- . FillAsync (
45- "43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L" ) ;
46- await s . Page . Locator ( "input#PrivateViewKey" )
47- . FillAsync ( "1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e" ) ;
48- await s . Page . Locator ( "input#RestoreHeight" ) . FillAsync ( "0" ) ;
49- await s . Page . Locator ( "input#WalletPassword" ) . FillAsync ( "pass123" ) ;
50- await s . Page . ClickAsync ( "button[name='command'][value='set-wallet-details']" ) ;
122+ await s . Page . Locator ( "text=No Wallet Configured" ) . WaitForAsync ( ) ;
123+ await s . Page . ClickAsync ( "a[href*='/connect/XMR']" ) ;
124+ await s . Page . Locator ( "input#WalletName" ) . WaitForAsync ( ) ;
125+ await CreateWalletViaForm (
126+ s ,
127+ walletName : "primary-wallet" ,
128+ address : "43Pnj6ZKGFTJhaLhiecSFfLfr64KPJZw7MyGH73T6PTDekBBvsTAaWEUSM4bmJqDuYLizhA13jQkMRPpz9VXBCBqQQb6y5L" ,
129+ viewKey : "1bfa03b0c78aa6bc8292cf160ec9875657d61e889c41d0ebe5c54fd3a2c4b40e" ,
130+ password : "pass123" ,
131+ restoreHeight : "0"
132+ ) ;
133+ Assert . True ( await WalletExists ( s , "primary-wallet" ) ) ;
134+ Assert . True ( await WalletIsActive ( s , "primary-wallet" ) ) ;
135+
51136 await s . Page . CheckAsync ( "#Enabled" ) ;
52137 await s . Page . SelectOptionAsync ( "#SettlementConfirmationThresholdChoice" , "2" ) ;
53138 await s . Page . ClickAsync ( "#SaveButton" ) ;
54- var classList = await s . Page
55- . Locator ( "svg.icon-checkmark" )
56- . GetAttributeAsync ( "class" ) ;
57- Assert . Contains ( "text-success" , classList ) ;
58-
59- // Set rate provider
60- await s . Page . Locator ( "#menu-item-General" ) . ClickAsync ( ) ;
61- await s . Page . Locator ( "#menu-item-Rates" ) . ClickAsync ( ) ;
62- await s . Page . FillAsync ( "#DefaultCurrencyPairs" , "BTC_USD,XMR_USD,XMR_BTC" ) ;
63- await s . Page . SelectOptionAsync ( "#PrimarySource_PreferredExchange" , "kraken" ) ;
64- await s . Page . Locator ( "#page-primary" ) . ClickAsync ( ) ;
65-
66- // Generate a new invoice
67- await s . Page . Locator ( "a.nav-link[href*='invoices']" ) . ClickAsync ( ) ;
68- await s . Page . Locator ( "#page-primary" ) . ClickAsync ( ) ;
69- await s . Page . FillAsync ( "#Amount" , "4.20" ) ;
70- await s . Page . FillAsync ( "#BuyerEmail" , "monero@monero.com" ) ;
71- await Task . Delay ( TimeSpan . FromSeconds ( 25 ) ) ; // wallet-rpc needs some time to sync. refactor this later
72- await s . Page . Locator ( "#page-primary" ) . ClickAsync ( ) ;
73-
74- // View the invoice
75- var href = await s . Page . Locator ( "a[href^='/i/']" ) . GetAttributeAsync ( "href" ) ;
76- var invoiceId = href ? . Split ( "/i/" ) . Last ( ) ;
77- await s . Page . Locator ( $ "a[href='/i/{ invoiceId } ']") . ClickAsync ( ) ;
78- await s . Page . ClickAsync ( "#DetailsToggle" ) ;
79-
80- // Verify the total fiat amount is $4.20
81- var totalFiat = await s . Page
82- . Locator ( "#PaymentDetails-TotalFiat dd.clipboard-button" )
83- . InnerTextAsync ( ) ;
84- Assert . Equal ( "$4.20" , totalFiat ) ;
139+ await s . Page . Locator ( "svg.icon-checkmark.text-success" ) . WaitForAsync ( ) ;
85140
86- await s . Page . GoBackAsync ( ) ;
87- await s . Page . Locator ( "a.nav-link[href*='monerolike/XMR']" ) . ClickAsync ( ) ;
141+ var invoiceId = await s . CreateInvoice ( s . StoreId , 10 , "USD" ) ;
142+ await s . GoToInvoiceCheckout ( invoiceId ) ;
88143
89- // Create a new account label
90- await s . Page . FillAsync ( "#NewAccountLabel" , "tst-account" ) ;
91- await s . Page . ClickAsync ( "button[name='command'][value='add-account']" ) ;
144+ var copyButton = s . Page . Locator ( "button[data-clipboard].clipboard-button" ) ;
145+ var moneroAddress = await copyButton . GetAttributeAsync ( "data-clipboard" ) ;
146+ Assert . NotNull ( moneroAddress ) ;
147+ Assert . True ( moneroAddress . StartsWith ( "8" ) , $ "Expected Monero address to start with 8, but got: { moneroAddress } ") ;
92148
93- // Select primary Account Index
149+ await s . GoToStore ( s . StoreId ) ;
94150 await s . Page . Locator ( "a.nav-link[href*='monerolike/XMR']" ) . ClickAsync ( ) ;
95- await s . Page . SelectOptionAsync ( "#AccountIndex" , "1" ) ;
151+ Assert . True ( await WalletIsActive ( s , "primary-wallet" ) ) ;
152+
153+ await s . Page . CheckAsync ( "#Enabled" ) ;
154+ await s . Page . SelectOptionAsync ( "#SettlementConfirmationThresholdChoice" , "3" ) ;
96155 await s . Page . ClickAsync ( "#SaveButton" ) ;
97156
98- // Verify selected account index
157+ await s . GoToStore ( s . StoreId ) ;
99158 await s . Page . Locator ( "a.nav-link[href*='monerolike/XMR']" ) . ClickAsync ( ) ;
100- var selectedValue = await s . Page . Locator ( "#AccountIndex" ) . InputValueAsync ( ) ;
101- Assert . Equal ( "1" , selectedValue ) ;
159+ await s . Page . Locator ( "#walletManagementMenu" ) . WaitForAsync ( ) ;
102160
103- // Select confirmation time to 0
104- await s . Page . SelectOptionAsync ( "#SettlementConfirmationThresholdChoice" , "3" ) ;
105- await s . Page . ClickAsync ( "#SaveButton" ) ;
161+ Assert . True ( await WalletExists ( s , "primary-wallet" ) ) ;
162+ Assert . True ( await WalletIsActive ( s , "primary-wallet" ) ) ;
163+ await s . Page . ClickAsync ( "#walletManagementMenu" ) ;
164+ await s . Page . ClickAsync ( "button[data-bs-target='#createWalletModal']" ) ;
165+ await s . Page . Locator ( "#createWalletModal input#WalletName" ) . WaitForAsync ( ) ;
106166
107- await CleanUp ( s ) ;
167+ await CreateWalletViaModal (
168+ s ,
169+ walletName : "secondary-wallet" ,
170+ address : "41u3R13W1UDhvHGNS1cqtoh4CKFkuZ5aWbkmkEskhm3SNsuaLQAeFtrXyd6Q2XAgwzf41CMC65u8fVWjB38RLAUb8AKJMw9" ,
171+ viewKey : "134af10334bd65ce91e015db3ea9f0b1abd1a9ed2fa378bc537498f4f52f6f0f" ,
172+ password : "pass456" ,
173+ restoreHeight : "100"
174+ ) ;
175+ Assert . True ( await WalletExists ( s , "primary-wallet" ) ) ;
176+ Assert . True ( await WalletExists ( s , "secondary-wallet" ) ) ;
177+ Assert . True ( await WalletIsActive ( s , "secondary-wallet" ) ) ;
178+
179+ await SwitchActiveWallet ( s , "primary-wallet" , "pass123" ) ;
180+ Assert . True ( await WalletIsActive ( s , "primary-wallet" ) ) ;
181+ Assert . False ( await WalletIsActive ( s , "secondary-wallet" ) ) ;
182+
183+ await DeleteWallet ( s , "secondary-wallet" ) ;
184+ Assert . True ( await WalletExists ( s , "primary-wallet" ) ) ;
185+ Assert . False ( await WalletExists ( s , "secondary-wallet" ) ) ;
186+ Assert . True ( await WalletIsActive ( s , "primary-wallet" ) ) ;
108187 }
109188
189+
110190 [ Fact ]
111191 public async Task ShouldFailWhenWrongPrimaryAddress ( )
112192 {
0 commit comments