(Refactor) : Converted Selenium test for CanUsePairing and Others to playwright (#6927)

This commit is contained in:
Abhijay Jain
2025-11-05 13:54:58 +05:30
committed by GitHub
parent 849b27cf49
commit 999bd4db41
3 changed files with 220 additions and 185 deletions

View File

@@ -800,5 +800,28 @@ namespace BTCPayServer.Tests
await Page.FillAsync("#ConfirmInput", "DELETE");
await Page.ClickAsync("#ConfirmContinue");
}
public async Task AssertPageAccess(bool shouldHaveAccess, string url)
{
await GoToUrl(url);
await Page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
var content = await Page.ContentAsync();
Assert.DoesNotContain("404 - Page not found", content);
if (shouldHaveAccess)
{
Assert.DoesNotContain("- Denied</h", content);
// check associated link is active if present
var hrefToMatch = new Uri(Link(url), UriKind.Absolute).AbsolutePath.TrimEnd('/');
var sidebarLink = Page.Locator($"#mainNav a[href=\"{hrefToMatch}\"], #mainNav a[href=\"{hrefToMatch}/\"]");
if (await sidebarLink.CountAsync() > 0)
{
var classAttr = await sidebarLink.First.GetAttributeAsync("class");
Assert.Contains("active", classAttr);
}
}
else
{
Assert.Contains("- Denied</h", content);
}
}
}
}

View File

@@ -2241,6 +2241,203 @@ namespace BTCPayServer.Tests
await newPage.CloseAsync();
}
[Fact]
[Trait("Playwright", "Playwright")]
[Trait("Lightning", "Lightning")]
public async Task CanUsePredefinedRoles()
{
await using var s = CreatePlaywrightTester(newDb: true);
s.Server.ActivateLightning();
await s.StartAsync();
await s.Server.EnsureChannelsSetup();
var storeSettingsPaths = new [] {"settings", "rates", "checkout", "tokens", "users", "roles", "webhooks", "payout-processors",
"payout-processors/onchain-automated/BTC", "payout-processors/lightning-automated/BTC", "emails/rules", "email-settings", "forms"};
// Setup users
var manager = await s.RegisterNewUser();
await s.GoToHome();
await s.Logout();
await s.GoToRegister();
var employee = await s.RegisterNewUser();
await s.GoToHome();
await s.Logout();
await s.GoToRegister();
var guest = await s.RegisterNewUser();
await s.GoToHome();
await s.Logout();
await s.GoToRegister();
// Setup store, wallets and add users
await s.RegisterNewUser(true);
var (_, storeId) = await s.CreateNewStore();
await s.GoToStore();
await s.GenerateWallet(isHotWallet: true);
await s.AddLightningNode(LightningConnectionType.CLightning, false);
await s.AddUserToStore(storeId, manager, "Manager");
await s.AddUserToStore(storeId, employee, "Employee");
await s.AddUserToStore(storeId, guest, "Guest");
// Add apps
var (_, posId) = await s.CreateApp("PointOfSale");
var (_, crowdfundId) = await s.CreateApp("Crowdfund");
string GetStorePath(string subPath) => $"/stores/{storeId}" + (string.IsNullOrEmpty(subPath) ? "" : $"/{subPath}");
// Owner access
await s.AssertPageAccess(true, GetStorePath(""));
await s.AssertPageAccess(true, GetStorePath("reports"));
await s.AssertPageAccess(true, GetStorePath("invoices"));
await s.AssertPageAccess(true, GetStorePath("invoices/create"));
await s.AssertPageAccess(true, GetStorePath("payment-requests"));
await s.AssertPageAccess(true, GetStorePath("payment-requests/edit"));
await s.AssertPageAccess(true, GetStorePath("pull-payments"));
await s.AssertPageAccess(true, GetStorePath("payouts"));
await s.AssertPageAccess(true, GetStorePath("onchain/BTC"));
await s.AssertPageAccess(true, GetStorePath("onchain/BTC/settings"));
await s.AssertPageAccess(true, GetStorePath("lightning/BTC"));
await s.AssertPageAccess(true, GetStorePath("lightning/BTC/settings"));
await s.AssertPageAccess(true, GetStorePath("apps/create"));
await s.AssertPageAccess(true, $"/apps/{posId}/settings/pos");
await s.AssertPageAccess(true, $"/apps/{crowdfundId}/settings/crowdfund");
foreach (var path in storeSettingsPaths)
{ // should have manage access to settings, hence should see submit buttons or create links
s.TestLogs.LogInformation($"Checking access to store page {path} as owner");
await s.AssertPageAccess(true, $"/stores/{storeId}/{path}");
await s.Page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
if (path != "payout-processors")
{
var saveButton = s.Page.GetByRole(AriaRole.Button, new() { Name = "Save" });
if (await saveButton.CountAsync() > 0)
{
Assert.True(await saveButton.IsVisibleAsync());
}
}
}
await s.Logout();
// Manager access
await s.LogIn(manager);
await s.AssertPageAccess(false, GetStorePath(""));
await s.AssertPageAccess(true, GetStorePath("reports"));
await s.AssertPageAccess(true, GetStorePath("invoices"));
await s.AssertPageAccess(true, GetStorePath("invoices/create"));
await s.AssertPageAccess(true, GetStorePath("payment-requests"));
await s.AssertPageAccess(true, GetStorePath("payment-requests/edit"));
await s.AssertPageAccess(true, GetStorePath("pull-payments"));
await s.AssertPageAccess(true, GetStorePath("payouts"));
await s.AssertPageAccess(false, GetStorePath("onchain/BTC"));
await s.AssertPageAccess(false, GetStorePath("onchain/BTC/settings"));
await s.AssertPageAccess(false, GetStorePath("lightning/BTC"));
await s.AssertPageAccess(false, GetStorePath("lightning/BTC/settings"));
await s.AssertPageAccess(false, GetStorePath("apps/create"));
await s.AssertPageAccess(true, $"/apps/{posId}/settings/pos");
await s.AssertPageAccess(true, $"/apps/{crowdfundId}/settings/crowdfund");
foreach (var path in storeSettingsPaths)
{ // should have view access to settings, but no submit buttons or create links
s.TestLogs.LogInformation($"Checking access to store page {path} as manager");
await s.AssertPageAccess(true, $"/stores/{storeId}/{path}");
await s.Page.WaitForLoadStateAsync(LoadState.DOMContentLoaded);
Assert.False(await s.Page.GetByRole(AriaRole.Button, new() { Name = "Save" }).IsVisibleAsync());
}
await s.Logout();
// Employee access
await s.LogIn(employee);
await s.AssertPageAccess(false, GetStorePath(""));
await s.AssertPageAccess(false, GetStorePath("reports"));
await s.AssertPageAccess(true, GetStorePath("invoices"));
await s.AssertPageAccess(true, GetStorePath("invoices/create"));
await s.AssertPageAccess(true, GetStorePath("payment-requests"));
await s.AssertPageAccess(true, GetStorePath("payment-requests/edit"));
await s.AssertPageAccess(true, GetStorePath("pull-payments"));
await s.AssertPageAccess(true, GetStorePath("payouts"));
await s.AssertPageAccess(false, GetStorePath("onchain/BTC"));
await s.AssertPageAccess(false, GetStorePath("onchain/BTC/settings"));
await s.AssertPageAccess(false, GetStorePath("lightning/BTC"));
await s.AssertPageAccess(false, GetStorePath("lightning/BTC/settings"));
await s.AssertPageAccess(false, GetStorePath("apps/create"));
await s.AssertPageAccess(false, $"/apps/{posId}/settings/pos");
await s.AssertPageAccess(false, $"/apps/{crowdfundId}/settings/crowdfund");
foreach (var path in storeSettingsPaths)
{ // should not have access to settings
s.TestLogs.LogInformation($"Checking access to store page {path} as employee");
await s.AssertPageAccess(false, $"/stores/{storeId}/{path}");
}
await s.GoToHome();
await s.Logout();
// Guest access
await s.LogIn(guest);
await s.AssertPageAccess(false, GetStorePath(""));
await s.AssertPageAccess(false, GetStorePath("reports"));
await s.AssertPageAccess(true, GetStorePath("invoices"));
await s.AssertPageAccess(true, GetStorePath("invoices/create"));
await s.AssertPageAccess(true, GetStorePath("payment-requests"));
await s.AssertPageAccess(false, GetStorePath("payment-requests/edit"));
await s.AssertPageAccess(true, GetStorePath("pull-payments"));
await s.AssertPageAccess(true, GetStorePath("payouts"));
await s.AssertPageAccess(false, GetStorePath("onchain/BTC"));
await s.AssertPageAccess(false, GetStorePath("onchain/BTC/settings"));
await s.AssertPageAccess(false, GetStorePath("lightning/BTC"));
await s.AssertPageAccess(false, GetStorePath("lightning/BTC/settings"));
await s.AssertPageAccess(false, GetStorePath("apps/create"));
await s.AssertPageAccess(false, $"/apps/{posId}/settings/pos");
await s.AssertPageAccess(false, $"/apps/{crowdfundId}/settings/crowdfund");
foreach (var path in storeSettingsPaths)
{ // should not have access to settings
s.TestLogs.LogInformation($"Checking access to store page {path} as guest");
await s.AssertPageAccess(false, $"/stores/{storeId}/{path}");
}
await s.GoToHome();
await s.Logout();
}
[Fact]
public async Task CanUsePairing()
{
await using var s = CreatePlaywrightTester();
await s.StartAsync();
await s.Page.GotoAsync(s.Link("/api-access-request"));
Assert.Contains("ReturnUrl", s.Page.Url);
await s.GoToRegister();
await s.RegisterNewUser();
await s.CreateNewStore();
await s.AddDerivationScheme();
await s.GoToStore(s.StoreId, StoreNavPages.Tokens);
await s.Page.Locator("#CreateNewToken").ClickAsync();
await s.ClickPagePrimary();
var url = s.Page.Url;
var pairingCode = System.Text.RegularExpressions.Regex.Match(new Uri(url, UriKind.Absolute).Query, "pairingCode=([^&]*)").Groups[1].Value;
await s.ClickPagePrimary();
await s.FindAlertMessage();
Assert.Contains(pairingCode, await s.Page.ContentAsync());
var client = new NBitpayClient.Bitpay(new NBitcoin.Key(), s.ServerUri);
await client.AuthorizeClient(new NBitpayClient.PairingCode(pairingCode));
await client.CreateInvoiceAsync(
new NBitpayClient.Invoice() { Price = 1.000000012m, Currency = "USD", FullNotifications = true },
NBitpayClient.Facade.Merchant);
client = new NBitpayClient.Bitpay(new NBitcoin.Key(), s.ServerUri);
var code = await client.RequestClientAuthorizationAsync("hehe", NBitpayClient.Facade.Merchant);
await s.Page.GotoAsync(code.CreateLink(s.ServerUri).ToString());
await s.ClickPagePrimary();
await client.CreateInvoiceAsync(
new NBitpayClient.Invoice() { Price = 1.000000012m, Currency = "USD", FullNotifications = true },
NBitpayClient.Facade.Merchant);
await s.Page.GotoAsync(s.Link("/api-tokens"));
await s.ClickPagePrimary(); // Request
await s.ClickPagePrimary(); // Approve
var url2 = s.Page.Url;
var pairingCode2 = System.Text.RegularExpressions.Regex.Match(new Uri(url2, UriKind.Absolute).Query, "pairingCode=([^&]*)").Groups[1].Value;
Assert.False(string.IsNullOrEmpty(pairingCode2));
}
}
}

View File

@@ -297,51 +297,6 @@ namespace BTCPayServer.Tests
Assert.Contains("The store has been unarchived and will appear in the stores list by default again.", s.FindAlertMessage().Text);
}
[Fact(Timeout = TestTimeout)]
public async Task CanUsePairing()
{
using var s = CreateSeleniumTester();
await s.StartAsync();
s.Driver.Navigate().GoToUrl(s.Link("/api-access-request"));
Assert.Contains("ReturnUrl", s.Driver.Url);
s.GoToRegister();
s.RegisterNewUser();
s.CreateNewStore();
s.AddDerivationScheme();
s.GoToStore(StoreNavPages.Tokens);
s.Driver.FindElement(By.Id("CreateNewToken")).Click();
s.ClickPagePrimary();
var pairingCode = AssertUrlHasPairingCode(s);
s.ClickPagePrimary();
s.FindAlertMessage();
Assert.Contains(pairingCode, s.Driver.PageSource);
var client = new NBitpayClient.Bitpay(new Key(), s.ServerUri);
await client.AuthorizeClient(new NBitpayClient.PairingCode(pairingCode));
await client.CreateInvoiceAsync(
new NBitpayClient.Invoice() { Price = 1.000000012m, Currency = "USD", FullNotifications = true },
NBitpayClient.Facade.Merchant);
client = new NBitpayClient.Bitpay(new Key(), s.ServerUri);
var code = await client.RequestClientAuthorizationAsync("hehe", NBitpayClient.Facade.Merchant);
s.Driver.Navigate().GoToUrl(code.CreateLink(s.ServerUri));
s.ClickPagePrimary();
await client.CreateInvoiceAsync(
new NBitpayClient.Invoice() { Price = 1.000000012m, Currency = "USD", FullNotifications = true },
NBitpayClient.Facade.Merchant);
s.Driver.Navigate().GoToUrl(s.Link("/api-tokens"));
s.ClickPagePrimary(); // Request
s.ClickPagePrimary(); // Approve
AssertUrlHasPairingCode(s);
}
[Fact(Timeout = TestTimeout)]
public async Task CanCreateAppPoS()
{
@@ -1907,146 +1862,6 @@ retry:
}
}
[Fact]
[Trait("Selenium", "Selenium")]
[Trait("Lightning", "Lightning")]
public async Task CanUsePredefinedRoles()
{
using var s = CreateSeleniumTester(newDb: true);
s.Server.ActivateLightning();
await s.StartAsync();
await s.Server.EnsureChannelsSetup();
var storeSettingsPaths = new [] {"settings", "rates", "checkout", "tokens", "users", "roles", "webhooks", "payout-processors",
"payout-processors/onchain-automated/BTC", "payout-processors/lightning-automated/BTC", "emails/rules", "email-settings", "forms"};
// Setup users
var manager = s.RegisterNewUser();
s.Logout();
s.GoToRegister();
var employee = s.RegisterNewUser();
s.Logout();
s.GoToRegister();
var guest = s.RegisterNewUser();
s.Logout();
s.GoToRegister();
// Setup store, wallets and add users
s.RegisterNewUser(true);
(_, string storeId) = s.CreateNewStore();
s.GoToStore();
s.GenerateWallet(isHotWallet: true);
s.AddLightningNode(LightningConnectionType.CLightning, false);
s.AddUserToStore(storeId, manager, "Manager");
s.AddUserToStore(storeId, employee, "Employee");
s.AddUserToStore(storeId, guest, "Guest");
// Add apps
(_, string posId) = s.CreateApp("PointOfSale");
(_, string crowdfundId) = s.CreateApp("Crowdfund");
string GetStorePath(string subPath) => $"/stores/{storeId}" + (string.IsNullOrEmpty(subPath) ? "" : $"/{subPath}");
// Owner access
s.AssertPageAccess(true, GetStorePath(""));
s.AssertPageAccess(true, GetStorePath("reports"));
s.AssertPageAccess(true, GetStorePath("invoices"));
s.AssertPageAccess(true, GetStorePath("invoices/create"));
s.AssertPageAccess(true, GetStorePath("payment-requests"));
s.AssertPageAccess(true, GetStorePath("payment-requests/edit"));
s.AssertPageAccess(true, GetStorePath("pull-payments"));
s.AssertPageAccess(true, GetStorePath("payouts"));
s.AssertPageAccess(true, GetStorePath("onchain/BTC"));
s.AssertPageAccess(true, GetStorePath("onchain/BTC/settings"));
s.AssertPageAccess(true, GetStorePath("lightning/BTC"));
s.AssertPageAccess(true, GetStorePath("lightning/BTC/settings"));
s.AssertPageAccess(true, GetStorePath("apps/create"));
s.AssertPageAccess(true, $"/apps/{posId}/settings/pos");
s.AssertPageAccess(true, $"/apps/{crowdfundId}/settings/crowdfund");
foreach (var path in storeSettingsPaths)
{ // should have manage access to settings, hence should see submit buttons or create links
TestLogs.LogInformation($"Checking access to store page {path} as owner");
s.AssertPageAccess(true, $"stores/{storeId}/{path}");
if (path != "payout-processors")
{
s.Driver.FindElement(By.CssSelector("#mainContent .btn-primary"));
}
}
s.Logout();
// Manager access
s.LogIn(manager);
s.AssertPageAccess(false, GetStorePath(""));
s.AssertPageAccess(true, GetStorePath("reports"));
s.AssertPageAccess(true, GetStorePath("invoices"));
s.AssertPageAccess(true, GetStorePath("invoices/create"));
s.AssertPageAccess(true, GetStorePath("payment-requests"));
s.AssertPageAccess(true, GetStorePath("payment-requests/edit"));
s.AssertPageAccess(true, GetStorePath("pull-payments"));
s.AssertPageAccess(true, GetStorePath("payouts"));
s.AssertPageAccess(false, GetStorePath("onchain/BTC"));
s.AssertPageAccess(false, GetStorePath("onchain/BTC/settings"));
s.AssertPageAccess(false, GetStorePath("lightning/BTC"));
s.AssertPageAccess(false, GetStorePath("lightning/BTC/settings"));
s.AssertPageAccess(false, GetStorePath("apps/create"));
s.AssertPageAccess(true, $"/apps/{posId}/settings/pos");
s.AssertPageAccess(true, $"/apps/{crowdfundId}/settings/crowdfund");
foreach (var path in storeSettingsPaths)
{ // should have view access to settings, but no submit buttons or create links
TestLogs.LogInformation($"Checking access to store page {path} as manager");
s.AssertPageAccess(true, $"stores/{storeId}/{path}");
s.Driver.ElementDoesNotExist(By.CssSelector("#mainContent .btn-primary"));
}
s.Logout();
// Employee access
s.LogIn(employee);
s.AssertPageAccess(false, GetStorePath(""));
s.AssertPageAccess(false, GetStorePath("reports"));
s.AssertPageAccess(true, GetStorePath("invoices"));
s.AssertPageAccess(true, GetStorePath("invoices/create"));
s.AssertPageAccess(true, GetStorePath("payment-requests"));
s.AssertPageAccess(true, GetStorePath("payment-requests/edit"));
s.AssertPageAccess(true, GetStorePath("pull-payments"));
s.AssertPageAccess(true, GetStorePath("payouts"));
s.AssertPageAccess(false, GetStorePath("onchain/BTC"));
s.AssertPageAccess(false, GetStorePath("onchain/BTC/settings"));
s.AssertPageAccess(false, GetStorePath("lightning/BTC"));
s.AssertPageAccess(false, GetStorePath("lightning/BTC/settings"));
s.AssertPageAccess(false, GetStorePath("apps/create"));
s.AssertPageAccess(false, $"/apps/{posId}/settings/pos");
s.AssertPageAccess(false, $"/apps/{crowdfundId}/settings/crowdfund");
foreach (var path in storeSettingsPaths)
{ // should not have access to settings
TestLogs.LogInformation($"Checking access to store page {path} as employee");
s.AssertPageAccess(false, $"stores/{storeId}/{path}");
}
s.Logout();
// Guest access
s.LogIn(guest);
s.AssertPageAccess(false, GetStorePath(""));
s.AssertPageAccess(false, GetStorePath("reports"));
s.AssertPageAccess(true, GetStorePath("invoices"));
s.AssertPageAccess(true, GetStorePath("invoices/create"));
s.AssertPageAccess(true, GetStorePath("payment-requests"));
s.AssertPageAccess(false, GetStorePath("payment-requests/edit"));
s.AssertPageAccess(true, GetStorePath("pull-payments"));
s.AssertPageAccess(true, GetStorePath("payouts"));
s.AssertPageAccess(false, GetStorePath("onchain/BTC"));
s.AssertPageAccess(false, GetStorePath("onchain/BTC/settings"));
s.AssertPageAccess(false, GetStorePath("lightning/BTC"));
s.AssertPageAccess(false, GetStorePath("lightning/BTC/settings"));
s.AssertPageAccess(false, GetStorePath("apps/create"));
s.AssertPageAccess(false, $"/apps/{posId}/settings/pos");
s.AssertPageAccess(false, $"/apps/{crowdfundId}/settings/crowdfund");
foreach (var path in storeSettingsPaths)
{ // should not have access to settings
TestLogs.LogInformation($"Checking access to store page {path} as guest");
s.AssertPageAccess(false, $"stores/{storeId}/{path}");
}
s.Logout();
}
[Fact]
[Trait("Selenium", "Selenium")]
public async Task CanChangeUserRoles()