diff --git a/BTCPayServer.Tests/FeatureTests/MultisigTests.cs b/BTCPayServer.Tests/FeatureTests/MultisigTests.cs index 48b8607c7..6a1596ad2 100644 --- a/BTCPayServer.Tests/FeatureTests/MultisigTests.cs +++ b/BTCPayServer.Tests/FeatureTests/MultisigTests.cs @@ -35,24 +35,24 @@ public class MultisigTests : UnitTestBase await s.StartAsync(); var network = s.NetworkProvider.GetNetwork(cryptoCode); - var resp1 = generateWalletResp("tprv8ZgxMBicQKsPeGSkDtxjScBmmHP4rfSEPkf1vNmoqt5QjPTco2zPd6UVWkJf2fU8gdKPYRdDMizxtMRqmpVpxsWuqRxVs2d5VsEhwxaK3h7", + var resp1 = generateWalletResp("tprv8ZgxMBicQKsPeGSkDtxjScBmmHP4rfSEPkf1vNmoqt5QjPTco2zPd6UVWkJf2fU8gdKPYRdDMizxtMRqmpVpxsWuqRxVs2d5VsEhwxaK3h7", "57b3f43a/84'/1'/0'", "tpubDCzBHRPRcv7Y3utw1hZVrCar21gsj8vsXcehAG4z3R4NnmdMAASQwYYxGBd2f4q5s5ZFGvQBBFs1jVcGsXYoSTA1YFQPwizjsQLU12ibLyu", network); - var resp2 = generateWalletResp("tprv8ZgxMBicQKsPeC6Xuw83UJHgjnszEUjwH9E5f5FZ3fHgJHBQApo8CmFCsowcdwbRM119UnTqSzVWUsWGtLsxc8wnZa5L8xmEsvEpiyRj4Js", + var resp2 = generateWalletResp("tprv8ZgxMBicQKsPeC6Xuw83UJHgjnszEUjwH9E5f5FZ3fHgJHBQApo8CmFCsowcdwbRM119UnTqSzVWUsWGtLsxc8wnZa5L8xmEsvEpiyRj4Js", "ee7d36c4/84'/1'/0'", "tpubDCetxnEjn8HXA5NrDZbKKTUUYoWCVC2V3X7Kmh3o9UYTfh9c3wTPKyCyeUrLkQ8KHYptEsBoQq6AgqPZiW5neEgb2kjKEr41q1qSevoPFDM", network); - var resp3 = generateWalletResp("tprv8ZgxMBicQKsPekSniuKwLtXpB82dSDV8ZAK4uLUHxkiHWfDtR5yYwNZiicKdpT3UYwzTTMvXESCm45KyAiH7kiJY6yk51neC9ZvmwDpNsQh", + var resp3 = generateWalletResp("tprv8ZgxMBicQKsPekSniuKwLtXpB82dSDV8ZAK4uLUHxkiHWfDtR5yYwNZiicKdpT3UYwzTTMvXESCm45KyAiH7kiJY6yk51neC9ZvmwDpNsQh", "6c014fb3/84'/1'/0'", "tpubDCaTgjJfS5UEim6h66VpQBEZ2Tj6hHk8TzvL81HygdW1M8vZCRhUZLNhb3WTimyP2XMQRA3QGZPwwxUsEFQYK4EoRUWTcb9oB237FJ112tN", network); var multisigDerivationScheme = $"wsh(multi(2,[{resp1.AccountKeyPath}]{resp1.DerivationScheme}/0/*," + $"[{resp2.AccountKeyPath}]{resp2.DerivationScheme}/0/*," + $"[{resp3.AccountKeyPath}]{resp3.DerivationScheme}/0/*))"; - + var strategy = UIStoresController.ParseDerivationStrategy(multisigDerivationScheme, network); strategy.Source = "ManualDerivationScheme"; var derivationScheme = strategy.AccountDerivation; var testPSBT = "cHNidP8BAIkCAAAAAQmiSunnaKN7F4Jv5uHROfYbIZOckCck/Wo7gAQmi9hfAAAAAAD9////AtgbZgAAAAAAIgAgWCUFlU9eWkyxn0l0yQxs2rXQZ7d9Ry8LaYECaVC0TUGAlpgAAAAAACIAIFZxT+UIdhHZC4qFPhPQ6IXdX+44HIxCYcoh/bNOhB0hAAAAAAABAStAAf8AAAAAACIAIL2DDkfKwKHxZj2EKxXUd4uwf0IvPaCxUtAPq9snpq9TAQDqAgAAAAABAVuHuou9E5y6zUJaUreQD0wUeiPnT2aY+YU7QaPJOiQCAAAAAAD9////AkAB/wAAAAAAIgAgvYMOR8rAofFmPYQrFdR3i7B/Qi89oLFS0A+r2yemr1PM5AYpAQAAABYAFIlFupZkD07+GRo24WRS3IFcf+EuAkcwRAIgGi9wAcTfc0d0+j+Vg82aYklXCUsPg+g3jS+PTBTSQwkCIAPh5CZF18DTBKqWU2qdhNCbZ8Tp/NCEHjLJRHcH0oluASECWnI1s9ozQRL2qbK6JbLHzj9LlU9Pras3nZfq/njBJwhwAAAAAQVpUiECMCCasr2FRmRMiWkM/l1iraFR18td5SZ2APyQiaI0yY8hA8K96vH64BelUJiEPGwM6UTwRSfAJUR2j8dkw7i31fFTIQMlHLlaAPxw3fl1vaM1EofIirt79MXOryM54zpHwu1GlVOuIgIDwr3q8frgF6VQmIQ8bAzpRPBFJ8AlRHaPx2TDuLfV8VNHMEQCIANnprskJz8oVsetqOEViHtzhmSG8c36r3zmUIHwIoOhAiAZ1jBqj40iu2S/nMfiGyuCC/jSiSGik7YVwiwN+bbxPAEiBgIwIJqyvYVGZEyJaQz+XWKtoVHXy13lJnYA/JCJojTJjxhXs/Q6VAAAgAEAAIAAAACAAAAAAAUAAAAiBgMlHLlaAPxw3fl1vaM1EofIirt79MXOryM54zpHwu1GlRhsAU+zVAAAgAEAAIAAAACAAAAAAAUAAAAiBgPCverx+uAXpVCYhDxsDOlE8EUnwCVEdo/HZMO4t9XxUxjufTbEVAAAgAEAAIAAAACAAAAAAAUAAAAAAQFpUiEDa/J6SaiRjP1jhq9jpNxFKovEuWBz28seNMvsn0JC/ZIhA7p3bS7vLYB5UxlNN6YqkEDITyaMlk/i450q6+4woveAIQPTchIOrd+TNGBOX6il1HRZnBndyRoUj/hahbjTaAGHglOuIgIDa/J6SaiRjP1jhq9jpNxFKovEuWBz28seNMvsn0JC/ZIYV7P0OlQAAIABAACAAAAAgAEAAAABAAAAIgIDundtLu8tgHlTGU03piqQQMhPJoyWT+LjnSrr7jCi94AY7n02xFQAAIABAACAAAAAgAEAAAABAAAAIgID03ISDq3fkzRgTl+opdR0WZwZ3ckaFI/4WoW402gBh4IYbAFPs1QAAIABAACAAAAAgAEAAAABAAAAAAEBaVIhA/fCRR3MWwCgNuXMvlWLonY+TurUKOHXOSHALCck62deIQPqeQXD8ws9SDEDXSyD6a3WFlIGH+gDUf2/xAfw8HxE8iEC3LBRJYYxRzIeg9NxLGvtfATvFaKsO9D7AUjoTLZzke5TriICAtywUSWGMUcyHoPTcSxr7XwE7xWirDvQ+wFI6Ey2c5HuGGwBT7NUAACAAQAAgAAAAIAAAAAADAAAACICA+p5BcPzCz1IMQNdLIPprdYWUgYf6ANR/b/EB/DwfETyGO59NsRUAACAAQAAgAAAAIAAAAAADAAAACICA/fCRR3MWwCgNuXMvlWLonY+TurUKOHXOSHALCck62deGFez9DpUAACAAQAAgAAAAIAAAAAADAAAAAA="; - + var signedPsbt = SignWithSeed(testPSBT, derivationScheme, resp1); s.TestLogs.LogInformation($"Signed PSBT: {signedPsbt}"); } @@ -62,22 +62,22 @@ public class MultisigTests : UnitTestBase public async Task CanEnableAndUseMultisigWallet() { var cryptoCode = "BTC"; - using var s = CreatePlaywrightTester(); + await using var s = CreatePlaywrightTester(); await s.StartAsync(); await s.RegisterNewUser(true); var network = s.Server.NetworkProvider.GetNetwork(cryptoCode); - var resp1 = generateWalletResp("tprv8ZgxMBicQKsPeGSkDtxjScBmmHP4rfSEPkf1vNmoqt5QjPTco2zPd6UVWkJf2fU8gdKPYRdDMizxtMRqmpVpxsWuqRxVs2d5VsEhwxaK3h7", + var resp1 = generateWalletResp("tprv8ZgxMBicQKsPeGSkDtxjScBmmHP4rfSEPkf1vNmoqt5QjPTco2zPd6UVWkJf2fU8gdKPYRdDMizxtMRqmpVpxsWuqRxVs2d5VsEhwxaK3h7", "57b3f43a/84'/1'/0'", "tpubDCzBHRPRcv7Y3utw1hZVrCar21gsj8vsXcehAG4z3R4NnmdMAASQwYYxGBd2f4q5s5ZFGvQBBFs1jVcGsXYoSTA1YFQPwizjsQLU12ibLyu", network); - var resp2 = generateWalletResp("tprv8ZgxMBicQKsPeC6Xuw83UJHgjnszEUjwH9E5f5FZ3fHgJHBQApo8CmFCsowcdwbRM119UnTqSzVWUsWGtLsxc8wnZa5L8xmEsvEpiyRj4Js", + var resp2 = generateWalletResp("tprv8ZgxMBicQKsPeC6Xuw83UJHgjnszEUjwH9E5f5FZ3fHgJHBQApo8CmFCsowcdwbRM119UnTqSzVWUsWGtLsxc8wnZa5L8xmEsvEpiyRj4Js", "ee7d36c4/84'/1'/0'", "tpubDCetxnEjn8HXA5NrDZbKKTUUYoWCVC2V3X7Kmh3o9UYTfh9c3wTPKyCyeUrLkQ8KHYptEsBoQq6AgqPZiW5neEgb2kjKEr41q1qSevoPFDM", network); - var resp3 = generateWalletResp("tprv8ZgxMBicQKsPekSniuKwLtXpB82dSDV8ZAK4uLUHxkiHWfDtR5yYwNZiicKdpT3UYwzTTMvXESCm45KyAiH7kiJY6yk51neC9ZvmwDpNsQh", + var resp3 = generateWalletResp("tprv8ZgxMBicQKsPekSniuKwLtXpB82dSDV8ZAK4uLUHxkiHWfDtR5yYwNZiicKdpT3UYwzTTMvXESCm45KyAiH7kiJY6yk51neC9ZvmwDpNsQh", "6c014fb3/84'/1'/0'", "tpubDCaTgjJfS5UEim6h66VpQBEZ2Tj6hHk8TzvL81HygdW1M8vZCRhUZLNhb3WTimyP2XMQRA3QGZPwwxUsEFQYK4EoRUWTcb9oB237FJ112tN", network); var multisigDerivationScheme = $"wsh(multi(2,[{resp1.AccountKeyPath}]{resp1.DerivationScheme}/0/*," + $"[{resp2.AccountKeyPath}]{resp2.DerivationScheme}/0/*," + $"[{resp3.AccountKeyPath}]{resp3.DerivationScheme}/0/*))"; - + var strategy = UIStoresController.ParseDerivationStrategy(multisigDerivationScheme, network); strategy.Source = "ManualDerivationScheme"; var derivationScheme = strategy.AccountDerivation; @@ -89,7 +89,7 @@ public class MultisigTests : UnitTestBase await s.Page.ClickAsync("#Continue"); await s.Page.ClickAsync("#Confirm"); s.TestLogs.LogInformation($"Multisig wallet setup: {multisigDerivationScheme}"); - + // fetch address from receive page await s.Page.ClickAsync("#WalletNav-Receive"); @@ -106,15 +106,15 @@ public class MultisigTests : UnitTestBase var amount = "0.1"; await s.Page.FillAsync("#Outputs_0__Amount", amount); await s.Page.ClickAsync("#CreatePendingTransaction"); - + // validating the state of UI Assert.Equal("0", await s.Page.TextContentAsync("#Sigs_0__Collected")); Assert.Equal("2/3", await s.Page.TextContentAsync("#Sigs_0__Scheme")); - + // now proceeding to click on sign button and sign transactions await SignPendingTransactionWithKey(s, address, derivationScheme, resp1); Assert.Equal("1", await s.Page.TextContentAsync("#Sigs_0__Collected")); - + await SignPendingTransactionWithKey(s, address, derivationScheme, resp2); Assert.Equal("2", await s.Page.TextContentAsync("#Sigs_0__Collected")); @@ -122,10 +122,10 @@ public class MultisigTests : UnitTestBase await s.Page.ClickAsync("//a[text()='Broadcast']"); await s.Page.ClickAsync("#BroadcastTransaction"); await s.FindAlertMessage(partialText: "Transaction broadcasted successfully"); - + // now that we broadcast transaction, there shouldn't be broadcast button Assert.False(await s.Page.Locator("//a[text()='Broadcast']").IsVisibleAsync()); - + // Abort pending transaction flow await s.Page.ClickAsync("#WalletNav-Send"); await s.Page.FillAsync("#Outputs_0__DestinationAddress", address); @@ -136,7 +136,7 @@ public class MultisigTests : UnitTestBase await s.Page.ClickAsync("#ConfirmContinue"); await s.FindAlertMessage(partialText: "Aborted Pending Transaction"); - + s.TestLogs.LogInformation($"Finished MultiSig Flow"); } @@ -154,7 +154,7 @@ public class MultisigTests : UnitTestBase await s.Page.ClickAsync("#ShowRawVersion"); var psbt = await s.Page.Locator("#psbt-base64").TextContentAsync(); - + // signing PSBT and entering it to submit var signedPsbt = SignWithSeed(psbt, derivationScheme, signingKey); @@ -169,10 +169,10 @@ public class MultisigTests : UnitTestBase var key1 = new BitcoinExtKey( ExtKey.Parse(tpriv, Network.RegTest), Network.RegTest); - - + + var parser = new DerivationSchemeParser(network); - + var resp1 = new GenerateWalletResponse { MasterHDKey = key1, @@ -192,13 +192,13 @@ public class MultisigTests : UnitTestBase var strKeypath = resp.AccountKeyPath.ToStringWithEmptyKeyPathAware(); RootedKeyPath rootedKeyPath = RootedKeyPath.Parse(strKeypath); - + if (rootedKeyPath.MasterFingerprint != extKey.GetPublicKey().GetHDFingerPrint()) throw new Exception("Master fingerprint mismatch. Ensure the wallet matches the PSBT."); // finished setting variables, now onto signing - + var psbt = PSBT.Parse(psbtBase64, Network.RegTest); - + // Sign the PSBT extKey = extKey.Derive(rootedKeyPath.KeyPath); psbt.Settings.SigningOptions = new SigningOptions(); diff --git a/BTCPayServer.Tests/PSBTTests.cs b/BTCPayServer.Tests/PSBTTests.cs index 6c9d20054..888dbf70a 100644 --- a/BTCPayServer.Tests/PSBTTests.cs +++ b/BTCPayServer.Tests/PSBTTests.cs @@ -23,7 +23,7 @@ namespace BTCPayServer.Tests [Trait("Playwright", "Playwright")] public async Task CanPlayWithPSBT() { - using var s = CreatePlaywrightTester(newDb: true); + await using var s = CreatePlaywrightTester(newDb: true); await s.StartAsync(); await s.RegisterNewUser(true); diff --git a/BTCPayServer.Tests/PlaywrightTester.cs b/BTCPayServer.Tests/PlaywrightTester.cs index 19961b07c..49578e08f 100644 --- a/BTCPayServer.Tests/PlaywrightTester.cs +++ b/BTCPayServer.Tests/PlaywrightTester.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Globalization; using System.IO; using System.Linq; @@ -19,7 +20,7 @@ using Xunit; namespace BTCPayServer.Tests { - public class PlaywrightTester : IDisposable + public class PlaywrightTester : IAsyncDisposable { public Uri ServerUri; private string CreatedUser; @@ -256,7 +257,7 @@ namespace BTCPayServer.Tests { await Page.ClickAsync("#ActionsDropdownToggle"); await Page.ClickAsync("#ChangeWalletLink"); - await Page.Locator("#ConfirmInput").FillAsync("REPLACE"); + await Page.FillAsync("#ConfirmInput", "REPLACE"); await Page.ClickAsync("#ConfirmContinue"); } @@ -265,7 +266,7 @@ namespace BTCPayServer.Tests TestLogs.LogInformation("Progressing with existing seed"); await Page.ClickAsync("#ImportWalletOptionsLink"); await Page.ClickAsync("#ImportSeedLink"); - await Page.Locator("#ExistingMnemonic").FillAsync(seed); + await Page.FillAsync("#ExistingMnemonic", seed); await Page.Locator("#SavePrivateKeys").SetCheckedAsync(isHotWallet); } else @@ -330,38 +331,46 @@ namespace BTCPayServer.Tests } public async Task LogIn(string user, string password = "123456") { - await Page.Locator("#Email").FillAsync(user); - await Page.Locator("#Password").FillAsync(password); - await Page.Locator("#LoginButton").ClickAsync(); + await Page.FillAsync("#Email", user); + await Page.FillAsync("#Password", password); + await Page.ClickAsync("#LoginButton"); } public async Task GoToProfile(ManageNavPages navPages = ManageNavPages.Index) { - await Page.Locator("#Nav-Account").ClickAsync(); - await Page.Locator("#Nav-ManageAccount").ClickAsync(); + await Page.ClickAsync("#Nav-Account"); + await Page.ClickAsync("#Nav-ManageAccount"); if (navPages != ManageNavPages.Index) { - await Page.Locator($"#SectionNav-{navPages.ToString()}").ClickAsync(); + await Page.ClickAsync($"#SectionNav-{navPages.ToString()}"); } } public async Task GoToServer(ServerNavPages navPages = ServerNavPages.Policies) { - await Page.Locator("#Nav-ServerSettings").ClickAsync(); + await Page.ClickAsync("#Nav-ServerSettings"); if (navPages != ServerNavPages.Policies) { - await Page.Locator($"#SectionNav-{navPages}").ClickAsync(); + await Page.ClickAsync($"#SectionNav-{navPages}"); } } - public async Task ClickOnAllSectionLinks() + public async Task ClickOnAllSectionLinks(string sectionSelector = "#SectionNav") { - var links = await Page.Locator("#SectionNav .nav-link").EvaluateAllAsync("els => els.map(e => e.href)"); - + List links = []; + foreach (var locator in await Page.Locator($"{sectionSelector} .nav-link").AllAsync()) + { + var link = await locator.GetAttributeAsync("href"); + if (link is null or "/logout") + continue; + Assert.NotNull(link); + links.Add(link); + } + Assert.NotEmpty(links); foreach (var link in links) { TestLogs.LogInformation($"Checking no error on {link}"); - await Page.GotoAsync(link); + await GoToUrl(link); await Page.AssertNoError(); } } @@ -429,24 +438,29 @@ namespace BTCPayServer.Tests } - public void Dispose() + + public async ValueTask DisposeAsync() { - static void Try(Action action) + static async Task Try(Func action) { try - { action(); } + { await action(); } catch { } } - Try(() => + await Try(async () => { - Page?.CloseAsync().GetAwaiter().GetResult(); + if (Page is null) + return; + await Page.CloseAsync(); Page = null; }); - Try(() => + await Try(async () => { - Browser?.CloseAsync().GetAwaiter().GetResult(); + if (Browser is null) + return; + await Browser.CloseAsync(); Browser = null; }); Server?.Dispose(); diff --git a/BTCPayServer.Tests/PlaywrightTests.cs b/BTCPayServer.Tests/PlaywrightTests.cs index 679e12813..8f994aabb 100644 --- a/BTCPayServer.Tests/PlaywrightTests.cs +++ b/BTCPayServer.Tests/PlaywrightTests.cs @@ -24,13 +24,13 @@ namespace BTCPayServer.Tests [Fact] public async Task CanNavigateServerSettings() { - using var s = CreatePlaywrightTester(); + await using var s = CreatePlaywrightTester(); await s.StartAsync(); await s.RegisterNewUser(true); await s.GoToHome(); await s.GoToServer(); await s.Page.AssertNoError(); - await s.ClickOnAllSectionLinks(); + await s.ClickOnAllSectionLinks("#mainNavSettings"); await s.GoToServer(ServerNavPages.Services); s.TestLogs.LogInformation("Let's check if we can access the logs"); await s.Page.GetByRole(AriaRole.Link, new() { Name = "Logs" }).ClickAsync(); @@ -42,24 +42,22 @@ namespace BTCPayServer.Tests [Fact] public async Task CanUseForms() { - using var s = CreatePlaywrightTester(); + await using var s = CreatePlaywrightTester(); await s.StartAsync(); await s.InitializeBTCPayServer(); // Point Of Sale var appName = $"PoS-{Guid.NewGuid().ToString()[..21]}"; await s.Page.ClickAsync("#StoreNav-CreatePointOfSale"); - await s.Page.Locator("#AppName").FillAsync(appName); + await s.Page.FillAsync("#AppName", appName); await s.ClickPagePrimary(); - var textContent = await (await s.FindAlertMessage()).TextContentAsync(); - Assert.Contains("App successfully created", textContent); + await s.FindAlertMessage(partialText: "App successfully created"); await s.Page.SelectOptionAsync("#FormId", "Email"); await s.ClickPagePrimary(); - textContent = await (await s.FindAlertMessage()).TextContentAsync(); - Assert.Contains("App updated", textContent); + await s.FindAlertMessage(partialText: "App updated"); await s.Page.ClickAsync("#ViewApp"); var popOutPage = await s.Page.Context.WaitForPageAsync(); await popOutPage.Locator("button[type='submit']").First.ClickAsync(); - await popOutPage.Locator("[name='buyerEmail']").FillAsync("aa@aa.com"); + await popOutPage.FillAsync("[name='buyerEmail']", "aa@aa.com"); await popOutPage.ClickAsync("input[type='submit']"); await s.PayInvoiceAsync(popOutPage, true); var invoiceId = popOutPage.Url[(popOutPage.Url.LastIndexOf("/", StringComparison.Ordinal) + 1)..]; @@ -71,8 +69,8 @@ namespace BTCPayServer.Tests // Payment Request await s.Page.ClickAsync("#StoreNav-PaymentRequests"); await s.ClickPagePrimary(); - await s.Page.Locator("#Title").FillAsync("Pay123"); - await s.Page.Locator("#Amount").FillAsync("700"); + await s.Page.FillAsync("#Title", "Pay123"); + await s.Page.FillAsync("#Amount", "700"); await s.Page.SelectOptionAsync("#FormId", "Email"); await s.ClickPagePrimary(); await s.Page.Locator("a[id^='Edit-']").First.ClickAsync(); @@ -81,7 +79,7 @@ namespace BTCPayServer.Tests popOutPage = await s.Page.Context.WaitForPageAsync(); await popOutPage.ClickAsync("[data-test='form-button']"); Assert.Contains("Enter your email", await popOutPage.ContentAsync()); - await popOutPage.Locator("input[name='buyerEmail']").FillAsync("aa@aa.com"); + await popOutPage.FillAsync("input[name='buyerEmail']", "aa@aa.com"); await popOutPage.ClickAsync("#page-primary"); invoiceId = popOutPage.Url.Split('/').Last(); await popOutPage.CloseAsync(); @@ -96,19 +94,19 @@ namespace BTCPayServer.Tests await s.GoToStore(StoreNavPages.Forms); Assert.Contains("There are no forms yet.", await s.Page.ContentAsync()); await s.ClickPagePrimary(); - await s.Page.Locator("[name='Name']").FillAsync("Custom Form 1"); - await s.Page.Locator("#ApplyEmailTemplate").ClickAsync(); + await s.Page.FillAsync("[name='Name']", "Custom Form 1"); + await s.Page.ClickAsync("#ApplyEmailTemplate"); await s.Page.ClickAsync("#CodeTabButton"); await s.Page.Locator("#CodeTabPane").WaitForAsync(); var config = await s.Page.Locator("[name='FormConfig']").InputValueAsync(); Assert.Contains("buyerEmail", config); await s.Page.Locator("[name='FormConfig']").ClearAsync(); - await s.Page.Locator("[name='FormConfig']").FillAsync(config.Replace("Enter your email", "CustomFormInputTest")); + await s.Page.FillAsync("[name='FormConfig']", config.Replace("Enter your email", "CustomFormInputTest")); await s.ClickPagePrimary(); await s.Page.ClickAsync("#ViewForm"); var formUrl = s.Page.Url; Assert.Contains("CustomFormInputTest", await s.Page.ContentAsync()); - await s.Page.Locator("[name='buyerEmail']").FillAsync("aa@aa.com"); + await s.Page.FillAsync("[name='buyerEmail']", "aa@aa.com"); await s.Page.ClickAsync("input[type='submit']"); await s.PayInvoiceAsync(s.Page, true, 0.001m); var result = await s.Server.PayTester.HttpClient.GetAsync(formUrl); @@ -118,17 +116,17 @@ namespace BTCPayServer.Tests await s.GoToStore(StoreNavPages.Forms); Assert.Contains("Custom Form 1", await s.Page.ContentAsync()); await s.Page.GetByRole(AriaRole.Link, new() { Name = "Remove" }).ClickAsync(); - await s.Page.Locator("#ConfirmInput").FillAsync("DELETE"); + await s.Page.FillAsync("#ConfirmInput", "DELETE"); await s.Page.ClickAsync("#ConfirmContinue"); Assert.DoesNotContain("Custom Form 1", await s.Page.ContentAsync()); await s.ClickPagePrimary(); - await s.Page.Locator("[name='Name']").FillAsync("Custom Form 2"); + await s.Page.FillAsync("[name='Name']", "Custom Form 2"); await s.Page.ClickAsync("#ApplyEmailTemplate"); await s.Page.ClickAsync("#CodeTabButton"); await s.Page.Locator("#CodeTabPane").WaitForAsync(); await s.Page.Locator("input[type='checkbox'][name='Public']").SetCheckedAsync(true); await s.Page.Locator("[name='FormConfig']").ClearAsync(); - await s.Page.Locator("[name='FormConfig']").FillAsync(config.Replace("Enter your email", "CustomFormInputTest2")); + await s.Page.FillAsync("[name='FormConfig']", config.Replace("Enter your email", "CustomFormInputTest2")); await s.ClickPagePrimary(); await s.Page.ClickAsync("#ViewForm"); formUrl = s.Page.Url; @@ -140,7 +138,7 @@ namespace BTCPayServer.Tests Assert.Contains("Custom Form 2", await s.Page.ContentAsync()); await s.Page.GetByRole(AriaRole.Link, new() { Name = "Custom Form 2" }).ClickAsync(); await s.Page.Locator("[name='Name']").ClearAsync(); - await s.Page.Locator("[name='Name']").FillAsync("Custom Form 3"); + await s.Page.FillAsync("[name='Name']", "Custom Form 3"); await s.ClickPagePrimary(); await s.GoToStore(StoreNavPages.Forms); Assert.Contains("Custom Form 3", await s.Page.ContentAsync()); @@ -154,7 +152,7 @@ namespace BTCPayServer.Tests [Fact] public async Task CanChangeUserMail() { - using var s = CreatePlaywrightTester(); + await using var s = CreatePlaywrightTester(); await s.StartAsync(); var tester = s.Server; var u1 = tester.NewAccount(); @@ -167,14 +165,13 @@ namespace BTCPayServer.Tests await s.LogIn(u1.RegisterDetails.Email, u1.RegisterDetails.Password); await s.GoToProfile(); await s.Page.Locator("#Email").ClearAsync(); - await s.Page.Locator("#Email").FillAsync(u2.RegisterDetails.Email); + await s.Page.FillAsync("#Email", u2.RegisterDetails.Email); await s.ClickPagePrimary(); - var alert = await s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error); - Assert.Contains("The email address is already in use with an other account.", await alert.TextContentAsync()); + await s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error, partialText: "The email address is already in use with an other account."); await s.GoToProfile(); await s.Page.Locator("#Email").ClearAsync(); var changedEmail = Guid.NewGuid() + "@lol.com"; - await s.Page.Locator("#Email").FillAsync(changedEmail); + await s.Page.FillAsync("#Email", changedEmail); await s.ClickPagePrimary(); await s.FindAlertMessage(); var manager = tester.PayTester.GetService>(); @@ -185,7 +182,7 @@ namespace BTCPayServer.Tests [Fact] public async Task CanManageUsers() { - using var s = CreatePlaywrightTester(); + await using var s = CreatePlaywrightTester(); await s.StartAsync(); await s.RegisterNewUser(); var user = s.AsTestAccount(); @@ -198,67 +195,62 @@ namespace BTCPayServer.Tests // Manage user password reset await s.Page.Locator("#SearchTerm").ClearAsync(); - await s.Page.Locator("#SearchTerm").FillAsync(user.RegisterDetails.Email); + await s.Page.FillAsync("#SearchTerm", user.RegisterDetails.Email); await s.Page.Locator("#SearchTerm").PressAsync("Enter"); var rows = s.Page.Locator("#UsersList tr.user-overview-row"); Assert.Equal(1, await rows.CountAsync()); Assert.Contains(user.RegisterDetails.Email, await rows.First.TextContentAsync()); - await s.Page.Locator("#UsersList tr.user-overview-row:first-child .reset-password").ClickAsync(); - await s.Page.Locator("#Password").FillAsync("Password@1!"); - await s.Page.Locator("#ConfirmPassword").FillAsync("Password@1!"); + await s.Page.ClickAsync("#UsersList tr.user-overview-row:first-child .reset-password"); + await s.Page.FillAsync("#Password", "Password@1!"); + await s.Page.FillAsync("#ConfirmPassword", "Password@1!"); await s.ClickPagePrimary(); - var passwordSetAlert = await s.FindAlertMessage(); - Assert.Contains("Password successfully set", await passwordSetAlert.TextContentAsync()); + await s.FindAlertMessage(partialText: "Password successfully set"); // Manage user status (disable and enable) // Disable user await s.Page.Locator("#SearchTerm").ClearAsync(); - await s.Page.Locator("#SearchTerm").FillAsync(user.RegisterDetails.Email); + await s.Page.FillAsync("#SearchTerm", user.RegisterDetails.Email); await s.Page.Locator("#SearchTerm").PressAsync("Enter"); rows = s.Page.Locator("#UsersList tr.user-overview-row"); Assert.Equal(1, await rows.CountAsync()); Assert.Contains(user.RegisterDetails.Email, await rows.First.TextContentAsync()); - await s.Page.Locator("#UsersList tr.user-overview-row:first-child .disable-user").ClickAsync(); - await s.Page.Locator("#ConfirmContinue").ClickAsync(); - var disabledUserAlert = await s.FindAlertMessage(); - Assert.Contains("User disabled", await disabledUserAlert.TextContentAsync()); + await s.Page.ClickAsync("#UsersList tr.user-overview-row:first-child .disable-user"); + await s.Page.ClickAsync("#ConfirmContinue"); + await s.FindAlertMessage(partialText: "User disabled"); //Enable user await s.Page.Locator("#SearchTerm").ClearAsync(); - await s.Page.Locator("#SearchTerm").FillAsync(user.RegisterDetails.Email); + await s.Page.FillAsync("#SearchTerm", user.RegisterDetails.Email); await s.Page.Locator("#SearchTerm").PressAsync("Enter"); rows = s.Page.Locator("#UsersList tr.user-overview-row"); Assert.Equal(1, await rows.CountAsync()); Assert.Contains(user.RegisterDetails.Email, await rows.First.TextContentAsync()); - await s.Page.Locator("#UsersList tr.user-overview-row:first-child .enable-user").ClickAsync(); - await s.Page.Locator("#ConfirmContinue").ClickAsync(); - var enabledUserAlert = await s.FindAlertMessage(); - Assert.Contains("User enabled", await enabledUserAlert.TextContentAsync()); + await s.Page.ClickAsync("#UsersList tr.user-overview-row:first-child .enable-user"); + await s.Page.ClickAsync("#ConfirmContinue"); + await s.FindAlertMessage(partialText: "User enabled"); // Manage user details (edit) await s.Page.Locator("#SearchTerm").ClearAsync(); - await s.Page.Locator("#SearchTerm").FillAsync(user.RegisterDetails.Email); + await s.Page.FillAsync("#SearchTerm", user.RegisterDetails.Email); await s.Page.Locator("#SearchTerm").PressAsync("Enter"); rows = s.Page.Locator("#UsersList tr.user-overview-row"); Assert.Equal(1, await rows.CountAsync()); Assert.Contains(user.RegisterDetails.Email, await rows.First.TextContentAsync()); - await s.Page.Locator("#UsersList tr.user-overview-row:first-child .user-edit").ClickAsync(); - await s.Page.Locator("#Name").FillAsync("Test User"); + await s.Page.ClickAsync("#UsersList tr.user-overview-row:first-child .user-edit"); + await s.Page.FillAsync("#Name", "Test User"); await s.ClickPagePrimary(); - var editUserAlert = await s.FindAlertMessage(); - Assert.Contains("User successfully updated", await editUserAlert.TextContentAsync()); + await s.FindAlertMessage(partialText: "User successfully updated"); // Manage user deletion await s.GoToServer(ServerNavPages.Users); await s.Page.Locator("#SearchTerm").ClearAsync(); - await s.Page.Locator("#SearchTerm").FillAsync(user.RegisterDetails.Email); + await s.Page.FillAsync("#SearchTerm", user.RegisterDetails.Email); await s.Page.Locator("#SearchTerm").PressAsync("Enter"); rows = s.Page.Locator("#UsersList tr.user-overview-row"); Assert.Equal(1, await rows.CountAsync()); Assert.Contains(user.RegisterDetails.Email, await rows.First.TextContentAsync()); - await s.Page.Locator("#UsersList tr.user-overview-row:first-child .delete-user").ClickAsync(); - await s.Page.Locator("#ConfirmContinue").ClickAsync(); - var userDeletionAlert = await s.FindAlertMessage(); - Assert.Contains("User deleted", await userDeletionAlert.TextContentAsync()); + await s.Page.ClickAsync("#UsersList tr.user-overview-row:first-child .delete-user"); + await s.Page.ClickAsync("#ConfirmContinue"); + await s.FindAlertMessage(partialText: "User deleted"); await s.Page.AssertNoError(); } @@ -266,7 +258,7 @@ namespace BTCPayServer.Tests [Fact] public async Task CanUseSSHService() { - using var s = CreatePlaywrightTester(); + await using var s = CreatePlaywrightTester(); await s.StartAsync(); var settings = s.Server.PayTester.GetService(); var policies = await settings.GetSettingAsync() ?? new PoliciesSettings(); @@ -287,8 +279,8 @@ namespace BTCPayServer.Tests await s.GoToUrl("/server/services/ssh"); await s.Page.AssertNoError(); await s.Page.Locator("#SSHKeyFileContent").ClearAsync(); - await s.Page.Locator("#SSHKeyFileContent").FillAsync("tes't\r\ntest2"); - await s.Page.Locator("#submit").ClickAsync(); + await s.Page.FillAsync("#SSHKeyFileContent", "tes't\r\ntest2"); + await s.Page.ClickAsync("#submit"); await s.Page.AssertNoError(); var text = await s.Page.Locator("#SSHKeyFileContent").TextContentAsync(); @@ -298,15 +290,15 @@ namespace BTCPayServer.Tests Assert.True((await s.Page.ContentAsync()).Contains("authorized_keys has been updated", StringComparison.OrdinalIgnoreCase)); await s.Page.Locator("#SSHKeyFileContent").ClearAsync(); - await s.Page.Locator("#submit").ClickAsync(); + await s.Page.ClickAsync("#submit"); text = await s.Page.Locator("#SSHKeyFileContent").TextContentAsync(); Assert.DoesNotContain("test2", text); // Let's try to disable it now - await s.Page.Locator("#disable").ClickAsync(); - await s.Page.Locator("#ConfirmInput").FillAsync("DISABLE"); - await s.Page.Locator("#ConfirmContinue").ClickAsync(); + await s.Page.ClickAsync("#disable"); + await s.Page.FillAsync("#ConfirmInput", "DISABLE"); + await s.Page.ClickAsync("#ConfirmContinue"); await s.GoToUrl("/server/services/ssh"); Assert.True((await s.Page.ContentAsync()).Contains("404 - Page not found", StringComparison.OrdinalIgnoreCase)); @@ -321,7 +313,7 @@ namespace BTCPayServer.Tests [Fact] public async Task CanSetupEmailServer() { - using var s = CreatePlaywrightTester(); + await using var s = CreatePlaywrightTester(); await s.StartAsync(); await s.RegisterNewUser(true); await s.CreateNewStore(); @@ -331,8 +323,7 @@ namespace BTCPayServer.Tests if (await s.Page.Locator("#ResetPassword").IsVisibleAsync()) { await s.Page.ClickAsync("#ResetPassword"); - var responseAlert = await s.FindAlertMessage(); - Assert.Contains("Email server password reset", await responseAlert.TextContentAsync()); + await s.FindAlertMessage(partialText: "Email server password reset"); } await s.Page.Locator("#Settings_Login").ClearAsync(); await s.Page.Locator("#Settings_From").ClearAsync(); @@ -373,9 +364,9 @@ namespace BTCPayServer.Tests await s.Page.ClickAsync("#CreateEmailRule"); await s.Page.Locator("#Trigger").SelectOptionAsync(new[] { "InvoicePaymentSettled" }); - await s.Page.Locator("#To").FillAsync("test@gmail.com"); + await s.Page.FillAsync("#To", "test@gmail.com"); await s.Page.ClickAsync("#CustomerEmail"); - await s.Page.Locator("#Subject").FillAsync("Thanks!"); + await s.Page.FillAsync("#Subject", "Thanks!"); await s.Page.Locator(".note-editable").FillAsync("Your invoice is settled"); await s.Page.ClickAsync("#SaveEmailRules"); // we now have a rule @@ -389,7 +380,7 @@ namespace BTCPayServer.Tests [Fact] public async Task NewUserLogin() { - using var s = CreatePlaywrightTester(); + await using var s = CreatePlaywrightTester(); await s.StartAsync(); //Register & Log Out var email = await s.RegisterNewUser(); @@ -403,8 +394,8 @@ namespace BTCPayServer.Tests // We should be redirected to login //Same User Can Log Back In - await s.Page.Locator("#Email").FillAsync(email); - await s.Page.Locator("#Password").FillAsync("123456"); + await s.Page.FillAsync("#Email", email); + await s.Page.FillAsync("#Password", "123456"); await s.Page.ClickAsync("#LoginButton"); // We should be redirected to invoice @@ -419,21 +410,21 @@ namespace BTCPayServer.Tests //Change Password & Log Out var newPassword = "abc???"; await s.GoToProfile(ManageNavPages.ChangePassword); - await s.Page.Locator("#OldPassword").FillAsync("123456"); - await s.Page.Locator("#NewPassword").FillAsync(newPassword); - await s.Page.Locator("#ConfirmPassword").FillAsync(newPassword); + await s.Page.FillAsync("#OldPassword", "123456"); + await s.Page.FillAsync("#NewPassword", newPassword); + await s.Page.FillAsync("#ConfirmPassword", newPassword); await s.ClickPagePrimary(); await s.Logout(); await s.Page.AssertNoError(); //Log In With New Password - await s.Page.Locator("#Email").FillAsync(email); - await s.Page.Locator("#Password").FillAsync(newPassword); - await s.Page.Locator("#LoginButton").ClickAsync(); + await s.Page.FillAsync("#Email", email); + await s.Page.FillAsync("#Password", newPassword); + await s.Page.ClickAsync("#LoginButton"); await s.GoToHome(); await s.GoToProfile(); - await s.ClickOnAllSectionLinks(); + await s.ClickOnAllSectionLinks("#mainNavSettings"); //let's test invite link await s.Logout(); @@ -444,7 +435,7 @@ namespace BTCPayServer.Tests await s.ClickPagePrimary(); var usr = RandomUtils.GetUInt256().ToString().Substring(64 - 20) + "@a.com"; - await s.Page.Locator("#Email").FillAsync(usr); + await s.Page.FillAsync("#Email", usr); await s.ClickPagePrimary(); var url = await s.Page.Locator("#InvitationUrl").GetAttributeAsync("data-text"); Assert.NotNull(url); @@ -453,14 +444,12 @@ namespace BTCPayServer.Tests Assert.Equal("hidden", await s.Page.Locator("#Email").GetAttributeAsync("type")); Assert.Equal(usr, await s.Page.Locator("#Email").GetAttributeAsync("value")); Assert.Equal("Create Account", await s.Page.Locator("h4").TextContentAsync()); - var invitationAlert = await s.FindAlertMessage(StatusMessageModel.StatusSeverity.Info); - Assert.Contains("Invitation accepted. Please set your password.", await invitationAlert.TextContentAsync()); + await s.FindAlertMessage(StatusMessageModel.StatusSeverity.Info, partialText: "Invitation accepted. Please set your password."); - await s.Page.Locator("#Password").FillAsync("123456"); - await s.Page.Locator("#ConfirmPassword").FillAsync("123456"); + await s.Page.FillAsync("#Password", "123456"); + await s.Page.FillAsync("#ConfirmPassword", "123456"); await s.ClickPagePrimary(); - var accountCreationAlert = await s.FindAlertMessage(); - Assert.Contains("Account successfully created.", await accountCreationAlert.TextContentAsync()); + await s.FindAlertMessage(partialText: "Account successfully created."); // We should be logged in now await s.GoToHome(); @@ -468,9 +457,9 @@ namespace BTCPayServer.Tests //let's test delete user quickly while we're at it await s.GoToProfile(); - await s.Page.Locator("#delete-user").ClickAsync(); - await s.Page.Locator("#ConfirmInput").FillAsync("DELETE"); - await s.Page.Locator("#ConfirmContinue").ClickAsync(); + await s.Page.ClickAsync("#delete-user"); + await s.Page.FillAsync("#ConfirmInput", "DELETE"); + await s.Page.ClickAsync("#ConfirmContinue"); Assert.Contains("/login", s.Page.Url); } @@ -478,19 +467,19 @@ namespace BTCPayServer.Tests private static async Task CanSetupEmailCore(PlaywrightTester s) { await s.Page.Locator("#QuickFillDropdownToggle").ScrollIntoViewIfNeededAsync(); - await s.Page.Locator("#QuickFillDropdownToggle").ClickAsync(); - await s.Page.Locator("#quick-fill .dropdown-menu .dropdown-item:first-child").ClickAsync(); + await s.Page.ClickAsync("#QuickFillDropdownToggle"); + await s.Page.ClickAsync("#quick-fill .dropdown-menu .dropdown-item:first-child"); await s.Page.Locator("#Settings_Login").ClearAsync(); - await s.Page.Locator("#Settings_Login").FillAsync("test@gmail.com"); + await s.Page.FillAsync("#Settings_Login", "test@gmail.com"); await s.Page.Locator("#Settings_Password").ClearAsync(); - await s.Page.Locator("#Settings_Password").FillAsync("mypassword"); + await s.Page.FillAsync("#Settings_Password", "mypassword"); await s.Page.Locator("#Settings_From").ClearAsync(); - await s.Page.Locator("#Settings_From").FillAsync("Firstname Lastname "); + await s.Page.FillAsync("#Settings_From", "Firstname Lastname "); await s.ClickPagePrimary(); await s.FindAlertMessage(partialText: "Email settings saved"); Assert.Contains("Configured", await s.Page.ContentAsync()); await s.Page.Locator("#Settings_Login").ClearAsync(); - await s.Page.Locator("#Settings_Login").FillAsync("test_fix@gmail.com"); + await s.Page.FillAsync("#Settings_Login", "test_fix@gmail.com"); await s.ClickPagePrimary(); await s.FindAlertMessage(partialText: "Email settings saved"); Assert.Contains("Configured", await s.Page.ContentAsync()); diff --git a/BTCPayServer/Components/MainNav/Default.cshtml b/BTCPayServer/Components/MainNav/Default.cshtml index 4b9c3f148..8549b292c 100644 --- a/BTCPayServer/Components/MainNav/Default.cshtml +++ b/BTCPayServer/Components/MainNav/Default.cshtml @@ -107,13 +107,13 @@ @if (ViewData.IsCategoryActive(typeof(WalletsNavPages), scheme.WalletId.ToString()) || ViewData.IsPageActive([WalletsNavPages.Settings], scheme.WalletId.ToString()) || ViewData.IsPageActive([StoreNavPages.OnchainSettings], categoryId)) { @if (!scheme.ReadonlyWallet) - { + { } - + @@ -192,8 +192,8 @@ - + }