diff --git a/BTCPayServer.Tests/CheckoutUITests.cs b/BTCPayServer.Tests/CheckoutUITests.cs index 2f07bf847..047e30436 100644 --- a/BTCPayServer.Tests/CheckoutUITests.cs +++ b/BTCPayServer.Tests/CheckoutUITests.cs @@ -1,11 +1,10 @@ using System; using System.Threading.Tasks; -using BTCPayServer.Client.Models; using BTCPayServer.Payments; -using BTCPayServer.Tests.Logging; using BTCPayServer.Views.Stores; using NBitcoin; using OpenQA.Selenium; +using OpenQA.Selenium.Support.UI; using Xunit; using Xunit.Abstractions; @@ -20,37 +19,9 @@ namespace BTCPayServer.Tests { } - [Fact(Timeout = TestTimeout)] - public async Task CanUseLanguageDropdown() - { - using var s = CreateSeleniumTester(); - await s.StartAsync(); - s.GoToRegister(); - s.RegisterNewUser(); - s.CreateNewStore(); - s.AddDerivationScheme(); - - var invoiceId = s.CreateInvoice(); - s.GoToInvoiceCheckout(invoiceId); - Assert.True(s.Driver.FindElement(By.Id("DefaultLang")).FindElements(By.TagName("option")).Count > 1); - var payWithTextEnglish = s.Driver.FindElement(By.Id("pay-with-text")).Text; - - var prettyDropdown = s.Driver.FindElement(By.Id("prettydropdown-DefaultLang")); - prettyDropdown.Click(); - await Task.Delay(200); - prettyDropdown.FindElement(By.CssSelector("[data-value=\"da-DK\"]")).Click(); - await Task.Delay(1000); - Assert.NotEqual(payWithTextEnglish, s.Driver.FindElement(By.Id("pay-with-text")).Text); - s.Driver.Navigate().GoToUrl(s.Driver.Url + "?lang=da-DK"); - - Assert.NotEqual(payWithTextEnglish, s.Driver.FindElement(By.Id("pay-with-text")).Text); - - s.Driver.Quit(); - } - [Fact(Timeout = TestTimeout)] [Trait("Lightning", "Lightning")] - public async Task CanSetDefaultPaymentMethod() + public async Task CanConfigureCheckout() { using var s = CreateSeleniumTester(); s.Server.ActivateLightning(); @@ -59,37 +30,403 @@ namespace BTCPayServer.Tests s.RegisterNewUser(true); s.CreateNewStore(); s.AddLightningNode(); - s.AddDerivationScheme(); + // Use non-legacy derivation scheme + s.AddDerivationScheme("BTC", "tpubDD79XF4pzhmPSJ9AyUay9YbXAeD1c6nkUqC32pnKARJH6Ja5hGUfGc76V82ahXpsKqN6UcSGXMkzR34aZq4W23C6DAdZFaVrzWqzj24F8BC"); - var invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC-LN"); + // Configure store url + var storeUrl = "https://satoshisteaks.com/"; + var supportUrl = "https://support.satoshisteaks.com/{InvoiceId}/"; + s.GoToStore(); + s.Driver.FindElement(By.Id("StoreWebsite")).SendKeys(storeUrl); + s.Driver.FindElement(By.Id("Save")).Click(); + Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); + + s.GoToStore(StoreNavPages.CheckoutAppearance); + s.Driver.WaitForAndClick(By.Id("Presets")); + s.Driver.WaitForAndClick(By.Id("Presets_InStore")); + Assert.True(s.Driver.SetCheckbox(By.Id("ShowPayInWalletButton"), true)); + s.Driver.FindElement(By.Id("SupportUrl")).SendKeys(supportUrl); + s.Driver.FindElement(By.Id("Save")).SendKeys(Keys.Enter); + Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); + + // Top up/zero amount invoices + var invoiceId = s.CreateInvoice(amount: null); s.GoToInvoiceCheckout(invoiceId); - Assert.Equal("Lightning", s.Driver.FindElement(By.ClassName("payment__currencies")).Text); - s.Driver.Quit(); - } + Assert.Equal(2, s.Driver.FindElements(By.CssSelector(".payment-method")).Count); + Assert.Contains("Bitcoin", s.Driver.FindElement(By.CssSelector(".payment-method.active")).Text); + Assert.Contains("LNURL", s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Text); + var qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value"); + var clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard"); + var payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); + var address = s.Driver.FindElement(By.CssSelector("#Address_BTC-CHAIN .truncate-center-start")).Text; + Assert.StartsWith("bcrt", s.Driver.FindElement(By.CssSelector("#Address_BTC-CHAIN .truncate-center-start")).Text); + Assert.DoesNotContain("lightning=", payUrl); + Assert.Equal($"bitcoin:{address}", payUrl); + Assert.Equal($"bitcoin:{address}", clipboard); + Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue); + s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC-CHAIN")); - [Fact(Timeout = TestTimeout)] - [Trait("Lightning", "Lightning")] - public async Task CanUseLightningSatsFeature() - { - using var s = CreateSeleniumTester(); - s.Server.ActivateLightning(); - await s.StartAsync(); - s.GoToRegister(); - s.RegisterNewUser(true); - s.CreateNewStore(); - s.AddLightningNode(); + // Details should show exchange rate + s.Driver.ToggleCollapse("PaymentDetails"); + s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalPrice")); + s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalFiat")); + s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-AmountDue")); + Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); + Assert.Contains("sat/byte", s.Driver.FindElement(By.Id("PaymentDetails-RecommendedFee")).Text); + + // Switch to LNURL + s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Click(); + TestUtils.Eventually(() => + { + payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); + Assert.StartsWith("lightning:lnurl", payUrl); + Assert.StartsWith("lnurl", s.Driver.WaitForElement(By.CssSelector("#Lightning_BTC-CHAIN .truncate-center-start")).Text); + s.Driver.ElementDoesNotExist(By.Id("Address_BTC-CHAIN")); + }); + + // Default payment method + s.GoToHome(); + invoiceId = s.CreateInvoice(21000, "SATS", defaultPaymentMethod: "BTC-LN"); + s.GoToInvoiceCheckout(invoiceId); + Assert.Equal(2, s.Driver.FindElements(By.CssSelector(".payment-method")).Count); + Assert.Contains("Lightning", s.Driver.WaitForElement(By.CssSelector(".payment-method.active")).Text); + Assert.Contains("Bitcoin", s.Driver.WaitForElement(By.CssSelector(".payment-method")).Text); + qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value"); + clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard"); + payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); + address = s.Driver.FindElement(By.CssSelector("#Lightning_BTC-LN .truncate-center-start")).Text; + Assert.Equal($"lightning:{address}", payUrl); + Assert.Equal($"lightning:{address}", clipboard); + Assert.Equal($"lightning:{address.ToUpperInvariant()}", qrValue); + s.Driver.ElementDoesNotExist(By.Id("Address_BTC-CHAIN")); + + // Lightning amount in sats + Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text); + s.GoToHome(); s.GoToLightningSettings(); s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true); s.Driver.FindElement(By.Id("save")).Click(); Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text); - - var invoiceId = s.CreateInvoice(10, "USD", "a@g.com"); s.GoToInvoiceCheckout(invoiceId); - Assert.Contains("sats", s.Driver.FindElement(By.ClassName("buyerTotalLine")).Text); + Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text); + + // Details should not show exchange rate + s.Driver.ToggleCollapse("PaymentDetails"); + s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-ExchangeRate")); + s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalFiat")); + s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-RecommendedFee")); + Assert.Contains("21 000 sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); + Assert.Contains("21 000 sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); + + // Expire + var expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); + expirySeconds.Clear(); + expirySeconds.SendKeys("3"); + s.Driver.FindElement(By.Id("Expire")).Click(); + + TestUtils.Eventually(() => + { + var paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); + Assert.Contains("This invoice will expire in", paymentInfo.Text); + Assert.DoesNotContain("Please send", paymentInfo.Text); + }); + TestUtils.Eventually(() => + { + var expiredSection = s.Driver.FindElement(By.Id("unpaid")); + Assert.True(expiredSection.Displayed); + Assert.Contains("Invoice Expired", expiredSection.Text); + Assert.Contains("resubmit a payment", expiredSection.Text); + Assert.DoesNotContain("This invoice expired with partial payment", expiredSection.Text); + }); + Assert.True(s.Driver.ElementDoesNotExist(By.Id("ContactLink"))); + Assert.True(s.Driver.ElementDoesNotExist(By.Id("ReceiptLink"))); + Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href")); + + // Expire paid partial + s.GoToHome(); + invoiceId = s.CreateInvoice(2100, "EUR"); + s.GoToInvoiceCheckout(invoiceId); + await Task.Delay(200); + address = s.Driver.FindElement(By.CssSelector("#Address_BTC-CHAIN .truncate-center-start")).Text; + var amountFraction = "0.00001"; + await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Network.RegTest), + Money.Parse(amountFraction)); + await s.Server.ExplorerNode.GenerateAsync(1); + + expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); + expirySeconds.Clear(); + expirySeconds.SendKeys("3"); + s.Driver.FindElement(By.Id("Expire")).Click(); + + TestUtils.Eventually(() => + { + var paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); + Assert.Contains("The invoice hasn't been paid in full.", paymentInfo.Text); + Assert.Contains("Please send", paymentInfo.Text); + }); + TestUtils.Eventually(() => + { + var expiredSection = s.Driver.FindElement(By.Id("unpaid")); + Assert.True(expiredSection.Displayed); + Assert.Contains("Invoice Expired", expiredSection.Text); + Assert.Contains("This invoice expired with partial payment", expiredSection.Text); + Assert.DoesNotContain("resubmit a payment", expiredSection.Text); + }); + var contactLink = s.Driver.FindElement(By.Id("ContactLink")); + Assert.Equal("Contact us", contactLink.Text); + Assert.Matches(supportUrl.Replace("{InvoiceId}", invoiceId), contactLink.GetAttribute("href")); + Assert.True(s.Driver.ElementDoesNotExist(By.Id("ReceiptLink"))); + Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href")); + + // Test payment + s.GoToHome(); + invoiceId = s.CreateInvoice(); + s.GoToInvoiceCheckout(invoiceId); + + // Details + s.Driver.ToggleCollapse("PaymentDetails"); + var details = s.Driver.FindElement(By.CssSelector(".payment-details")); + Assert.Contains("Total Price", details.Text); + Assert.Contains("Total Fiat", details.Text); + Assert.Contains("Exchange Rate", details.Text); + Assert.Contains("Amount Due", details.Text); + Assert.Contains("Recommended Fee", details.Text); + Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); + Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); + Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); + Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); + + // Pay partial amount + await Task.Delay(200); + s.Driver.FindElement(By.Id("test-payment-amount")).Clear(); + s.Driver.FindElement(By.Id("test-payment-amount")).SendKeys("0.00001"); + + // Fake Pay + TestUtils.Eventually(() => + { + s.Driver.FindElement(By.Id("FakePayment")).Click(); + s.Driver.FindElement(By.Id("mine-block")).Click(); + var paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); + Assert.Contains("The invoice hasn't been paid in full", paymentInfo.Text); + Assert.Contains("Please send", paymentInfo.Text); + }); + + s.Driver.Navigate().Refresh(); + + // Pay full amount + s.PayInvoice(); + + // Processing + TestUtils.Eventually(() => + { + var processingSection = s.Driver.WaitForElement(By.Id("processing")); + Assert.True(processingSection.Displayed); + Assert.Contains("Payment Received", processingSection.Text); + Assert.Contains("Your payment has been received and is now processing", processingSection.Text); + }); + s.Driver.FindElement(By.Id("confetti")); + + // Mine + s.MineBlockOnInvoiceCheckout(); + TestUtils.Eventually(() => + { + Assert.Contains("Mined 1 block", + s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text); + }); + + // Settled + TestUtils.Eventually(() => + { + var settledSection = s.Driver.WaitForElement(By.Id("settled")); + Assert.True(settledSection.Displayed); + Assert.Contains("Invoice Paid", settledSection.Text); + }); + s.Driver.FindElement(By.Id("confetti")); + s.Driver.FindElement(By.Id("ReceiptLink")); + Assert.True(s.Driver.ElementDoesNotExist(By.Id("ContactLink"))); + Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href")); + + // BIP21 + s.GoToHome(); + s.GoToStore(StoreNavPages.CheckoutAppearance); + s.Driver.SetCheckbox(By.Id("OnChainWithLnInvoiceFallback"), true); + s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), false); + s.Driver.FindElement(By.Id("Save")).Click(); + Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); + + invoiceId = s.CreateInvoice(); + s.GoToInvoiceCheckout(invoiceId); + Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method"))); + Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text); + qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value"); + clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard"); + payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); + var copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC-CHAIN .truncate-center-start")).Text; + var copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC-CHAIN .truncate-center-start")).Text; + Assert.StartsWith($"bitcoin:{copyAddressOnchain}?amount=", payUrl); + Assert.Contains("?amount=", payUrl); + Assert.Contains("&lightning=", payUrl); + Assert.StartsWith("bcrt", copyAddressOnchain); + Assert.StartsWith("lnbcrt", copyAddressLightning); + Assert.StartsWith($"bitcoin:{copyAddressOnchain.ToUpperInvariant()}?amount=", qrValue); + Assert.Contains("&lightning=LNBCRT", qrValue); + Assert.Contains("&lightning=lnbcrt", clipboard); + Assert.Equal(clipboard, payUrl); + + // Check details + s.Driver.ToggleCollapse("PaymentDetails"); + Assert.Contains("1 BTC = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); + Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); + Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); + Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); + Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); + + // Switch to amount displayed in sats + s.GoToHome(); + s.GoToStore(StoreNavPages.CheckoutAppearance); + s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true); + s.Driver.FindElement(By.Id("Save")).Click(); + Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); + s.GoToInvoiceCheckout(invoiceId); + Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text); + + // Check details + s.Driver.ToggleCollapse("PaymentDetails"); + Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); + Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); + Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); + Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); + Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); + + // BIP21 with LN as default payment method + s.GoToHome(); + invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC-LN"); + s.GoToInvoiceCheckout(invoiceId); + Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method"))); + payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); + Assert.StartsWith("bitcoin:", payUrl); + Assert.Contains("&lightning=lnbcrt", payUrl); + + // Check details + s.Driver.ToggleCollapse("PaymentDetails"); + Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); + Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); + Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); + Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); + Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); + + // Ensure LNURL is enabled + s.GoToHome(); + s.GoToLightningSettings(); + Assert.True(s.Driver.FindElement(By.Id("LNURLEnabled")).Selected); + + // BIP21 with top-up invoice + invoiceId = s.CreateInvoice(amount: null); + s.GoToInvoiceCheckout(invoiceId); + Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method"))); + qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value"); + clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard"); + payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); + copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC-CHAIN .truncate-center-start")).Text; + copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC-CHAIN .truncate-center-start")).Text; + Assert.StartsWith($"bitcoin:{copyAddressOnchain}", payUrl); + Assert.Contains("?lightning=lnurl", payUrl); + Assert.DoesNotContain("amount=", payUrl); + Assert.StartsWith("bcrt", copyAddressOnchain); + Assert.StartsWith("lnurl", copyAddressLightning); + Assert.StartsWith($"bitcoin:{copyAddressOnchain.ToUpperInvariant()}?lightning=LNURL", qrValue); + Assert.Contains($"bitcoin:{copyAddressOnchain}?lightning=lnurl", clipboard); + Assert.Equal(clipboard, payUrl); + + // Check details + s.Driver.ToggleCollapse("PaymentDetails"); + Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); + Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); + s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalFiat")); + s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-AmountDue")); + s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalPrice")); + + // Expiry message should not show amount for top-up invoice + expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); + expirySeconds.Clear(); + expirySeconds.SendKeys("5"); + s.Driver.FindElement(By.Id("Expire")).Click(); + TestUtils.Eventually(() => + { + var paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); + Assert.Contains("This invoice will expire in", paymentInfo.Text); + Assert.Contains("00:0", paymentInfo.Text); + Assert.DoesNotContain("Please send", paymentInfo.Text); + }); + + // Configure countdown timer + s.GoToHome(); + invoiceId = s.CreateInvoice(); + s.GoToHome(); + s.GoToStore(StoreNavPages.CheckoutAppearance); + var displayExpirationTimer = s.Driver.FindElement(By.Id("DisplayExpirationTimer")); + Assert.Equal("5", displayExpirationTimer.GetAttribute("value")); + displayExpirationTimer.Clear(); + displayExpirationTimer.SendKeys("10"); + s.Driver.FindElement(By.Id("Save")).Click(); + Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); + + s.GoToInvoiceCheckout(invoiceId); + var paymentInfo = s.Driver.FindElement(By.Id("PaymentInfo")); + Assert.False(paymentInfo.Displayed); + Assert.DoesNotContain("This invoice will expire in", paymentInfo.Text); + + expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); + expirySeconds.Clear(); + expirySeconds.SendKeys("599"); + s.Driver.FindElement(By.Id("Expire")).Click(); + TestUtils.Eventually(() => + { + paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); + Assert.True(paymentInfo.Displayed); + Assert.Contains("This invoice will expire in", paymentInfo.Text); + Assert.Contains("09:5", paymentInfo.Text); + }); + + // Disable LNURL again + s.GoToHome(); + s.GoToLightningSettings(); + s.Driver.SetCheckbox(By.Id("LNURLEnabled"), false); + s.Driver.ScrollTo(By.Id("save")); + s.Driver.FindElement(By.Id("save")).Click(); + Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text); + + // Test: + // - NFC/LNURL-W available with just Lightning + // - BIP21 works correctly even though Lightning is default payment method + s.GoToHome(); + invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC-LN"); + s.GoToInvoiceCheckout(invoiceId); + Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method"))); + payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); + Assert.StartsWith("bitcoin:", payUrl); + Assert.Contains("&lightning=lnbcrt", payUrl); + + // Language Switch + var languageSelect = new SelectElement(s.Driver.FindElement(By.Id("DefaultLang"))); + Assert.Equal("English", languageSelect.SelectedOption.Text); + Assert.Equal("View Details", s.Driver.FindElement(By.Id("DetailsToggle")).Text); + Assert.DoesNotContain("lang=", s.Driver.Url); + languageSelect.SelectByText("Deutsch"); + Assert.Equal("Details anzeigen", s.Driver.FindElement(By.Id("DetailsToggle")).Text); + Assert.Contains("lang=de", s.Driver.Url); + + s.Driver.Navigate().Refresh(); + languageSelect = new SelectElement(s.Driver.WaitForElement(By.Id("DefaultLang"))); + Assert.Equal("Deutsch", languageSelect.SelectedOption.Text); + Assert.Equal("Details anzeigen", s.Driver.FindElement(By.Id("DetailsToggle")).Text); + languageSelect.SelectByText("English"); + Assert.Equal("View Details", s.Driver.FindElement(By.Id("DetailsToggle")).Text); + Assert.Contains("lang=en", s.Driver.Url); } [Fact(Timeout = TestTimeout)] - public async Task CanUseJSModal() + public async Task CanUseCheckoutAsModal() { using var s = CreateSeleniumTester(); await s.StartAsync(); @@ -102,24 +439,24 @@ namespace BTCPayServer.Tests var invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId); s.Driver.Navigate() .GoToUrl(new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}")); - TestUtils.Eventually(() => - { - Assert.True(s.Driver.FindElement(By.Name("btcpay")).Displayed); - }); + s.Driver.WaitUntilAvailable(By.Name("btcpay")); + + var frameElement = s.Driver.FindElement(By.Name("btcpay")); + Assert.True(frameElement.Displayed); + var iframe = s.Driver.SwitchTo().Frame(frameElement); + iframe.WaitUntilAvailable(By.Id("Checkout")); + await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(invoice .GetPaymentPrompt(PaymentTypes.CHAIN.GetPaymentMethodId("BTC")) .Destination, Network.RegTest), new Money(0.001m, MoneyUnit.BTC)); - IWebElement closebutton = null; TestUtils.Eventually(() => { - var frameElement = s.Driver.FindElement(By.Name("btcpay")); - var iframe = s.Driver.SwitchTo().Frame(frameElement); - closebutton = iframe.FindElement(By.ClassName("close-action")); - Assert.True(closebutton.Displayed); + var closeButton = iframe.FindElement(By.Id("close")); + Assert.True(closeButton.Displayed); + closeButton.Click(); }); - closebutton.Click(); s.Driver.AssertElementNotFound(By.Name("btcpay")); Assert.Equal(s.Driver.Url, new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}").ToString()); diff --git a/BTCPayServer.Tests/Checkoutv2Tests.cs b/BTCPayServer.Tests/Checkoutv2Tests.cs deleted file mode 100644 index ed7576914..000000000 --- a/BTCPayServer.Tests/Checkoutv2Tests.cs +++ /dev/null @@ -1,466 +0,0 @@ -using System; -using System.Threading.Tasks; -using BTCPayServer.Payments; -using BTCPayServer.Views.Stores; -using NBitcoin; -using OpenQA.Selenium; -using OpenQA.Selenium.Support.UI; -using Xunit; -using Xunit.Abstractions; - -namespace BTCPayServer.Tests -{ - [Trait("Selenium", "Selenium")] - [Collection(nameof(NonParallelizableCollectionDefinition))] - public class CheckoutV2Tests : UnitTestBase - { - private const int TestTimeout = TestUtils.TestTimeout; - - public CheckoutV2Tests(ITestOutputHelper helper) : base(helper) - { - } - - [Fact(Timeout = TestTimeout)] - [Trait("Lightning", "Lightning")] - public async Task CanConfigureCheckout() - { - using var s = CreateSeleniumTester(); - s.Server.ActivateLightning(); - await s.StartAsync(); - s.GoToRegister(); - s.RegisterNewUser(true); - s.CreateNewStore(); - s.AddLightningNode(); - // Use non-legacy derivation scheme - s.AddDerivationScheme("BTC", "tpubDD79XF4pzhmPSJ9AyUay9YbXAeD1c6nkUqC32pnKARJH6Ja5hGUfGc76V82ahXpsKqN6UcSGXMkzR34aZq4W23C6DAdZFaVrzWqzj24F8BC"); - - // Configure store url - var storeUrl = "https://satoshisteaks.com/"; - var supportUrl = "https://support.satoshisteaks.com/{InvoiceId}/"; - s.GoToStore(); - s.Driver.FindElement(By.Id("StoreWebsite")).SendKeys(storeUrl); - s.Driver.FindElement(By.Id("Save")).Click(); - Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); - - s.GoToStore(StoreNavPages.CheckoutAppearance); - s.Driver.WaitForAndClick(By.Id("Presets")); - s.Driver.WaitForAndClick(By.Id("Presets_InStore")); - Assert.True(s.Driver.SetCheckbox(By.Id("ShowPayInWalletButton"), true)); - s.Driver.FindElement(By.Id("SupportUrl")).SendKeys(supportUrl); - s.Driver.FindElement(By.Id("Save")).SendKeys(Keys.Enter); - Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); - - // Top up/zero amount invoices - var invoiceId = s.CreateInvoice(amount: null); - s.GoToInvoiceCheckout(invoiceId); - Assert.Equal(2, s.Driver.FindElements(By.CssSelector(".payment-method")).Count); - Assert.Contains("Bitcoin", s.Driver.FindElement(By.CssSelector(".payment-method.active")).Text); - Assert.Contains("LNURL", s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Text); - var qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value"); - var clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard"); - var payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); - var address = s.Driver.FindElement(By.CssSelector("#Address_BTC-CHAIN .truncate-center-start")).Text; - Assert.StartsWith("bcrt", s.Driver.FindElement(By.CssSelector("#Address_BTC-CHAIN .truncate-center-start")).Text); - Assert.DoesNotContain("lightning=", payUrl); - Assert.Equal($"bitcoin:{address}", payUrl); - Assert.Equal($"bitcoin:{address}", clipboard); - Assert.Equal($"bitcoin:{address.ToUpperInvariant()}", qrValue); - s.Driver.ElementDoesNotExist(By.Id("Lightning_BTC-CHAIN")); - - // Details should show exchange rate - s.Driver.ToggleCollapse("PaymentDetails"); - s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalPrice")); - s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalFiat")); - s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-AmountDue")); - Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); - Assert.Contains("sat/byte", s.Driver.FindElement(By.Id("PaymentDetails-RecommendedFee")).Text); - - // Switch to LNURL - s.Driver.FindElement(By.CssSelector(".payment-method:nth-child(2)")).Click(); - TestUtils.Eventually(() => - { - payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); - Assert.StartsWith("lightning:lnurl", payUrl); - Assert.StartsWith("lnurl", s.Driver.WaitForElement(By.CssSelector("#Lightning_BTC-CHAIN .truncate-center-start")).Text); - s.Driver.ElementDoesNotExist(By.Id("Address_BTC-CHAIN")); - }); - - // Default payment method - s.GoToHome(); - invoiceId = s.CreateInvoice(21000, "SATS", defaultPaymentMethod: "BTC-LN"); - s.GoToInvoiceCheckout(invoiceId); - Assert.Equal(2, s.Driver.FindElements(By.CssSelector(".payment-method")).Count); - Assert.Contains("Lightning", s.Driver.WaitForElement(By.CssSelector(".payment-method.active")).Text); - Assert.Contains("Bitcoin", s.Driver.WaitForElement(By.CssSelector(".payment-method")).Text); - qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value"); - clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard"); - payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); - address = s.Driver.FindElement(By.CssSelector("#Lightning_BTC-LN .truncate-center-start")).Text; - Assert.Equal($"lightning:{address}", payUrl); - Assert.Equal($"lightning:{address}", clipboard); - Assert.Equal($"lightning:{address.ToUpperInvariant()}", qrValue); - s.Driver.ElementDoesNotExist(By.Id("Address_BTC-CHAIN")); - - // Lightning amount in sats - Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text); - s.GoToHome(); - s.GoToLightningSettings(); - s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true); - s.Driver.FindElement(By.Id("save")).Click(); - Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text); - s.GoToInvoiceCheckout(invoiceId); - Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text); - - // Details should not show exchange rate - s.Driver.ToggleCollapse("PaymentDetails"); - s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-ExchangeRate")); - s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalFiat")); - s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-RecommendedFee")); - Assert.Contains("21 000 sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); - Assert.Contains("21 000 sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); - - // Expire - var expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); - expirySeconds.Clear(); - expirySeconds.SendKeys("3"); - s.Driver.FindElement(By.Id("Expire")).Click(); - - TestUtils.Eventually(() => - { - var paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); - Assert.Contains("This invoice will expire in", paymentInfo.Text); - Assert.DoesNotContain("Please send", paymentInfo.Text); - }); - TestUtils.Eventually(() => - { - var expiredSection = s.Driver.FindElement(By.Id("unpaid")); - Assert.True(expiredSection.Displayed); - Assert.Contains("Invoice Expired", expiredSection.Text); - Assert.Contains("resubmit a payment", expiredSection.Text); - Assert.DoesNotContain("This invoice expired with partial payment", expiredSection.Text); - }); - Assert.True(s.Driver.ElementDoesNotExist(By.Id("ContactLink"))); - Assert.True(s.Driver.ElementDoesNotExist(By.Id("ReceiptLink"))); - Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href")); - - // Expire paid partial - s.GoToHome(); - invoiceId = s.CreateInvoice(2100, "EUR"); - s.GoToInvoiceCheckout(invoiceId); - await Task.Delay(200); - address = s.Driver.FindElement(By.CssSelector("#Address_BTC-CHAIN .truncate-center-start")).Text; - var amountFraction = "0.00001"; - await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(address, Network.RegTest), - Money.Parse(amountFraction)); - await s.Server.ExplorerNode.GenerateAsync(1); - - expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); - expirySeconds.Clear(); - expirySeconds.SendKeys("3"); - s.Driver.FindElement(By.Id("Expire")).Click(); - - TestUtils.Eventually(() => - { - var paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); - Assert.Contains("The invoice hasn't been paid in full.", paymentInfo.Text); - Assert.Contains("Please send", paymentInfo.Text); - }); - TestUtils.Eventually(() => - { - var expiredSection = s.Driver.FindElement(By.Id("unpaid")); - Assert.True(expiredSection.Displayed); - Assert.Contains("Invoice Expired", expiredSection.Text); - Assert.Contains("This invoice expired with partial payment", expiredSection.Text); - Assert.DoesNotContain("resubmit a payment", expiredSection.Text); - }); - var contactLink = s.Driver.FindElement(By.Id("ContactLink")); - Assert.Equal("Contact us", contactLink.Text); - Assert.Matches(supportUrl.Replace("{InvoiceId}", invoiceId), contactLink.GetAttribute("href")); - Assert.True(s.Driver.ElementDoesNotExist(By.Id("ReceiptLink"))); - Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href")); - - // Test payment - s.GoToHome(); - invoiceId = s.CreateInvoice(); - s.GoToInvoiceCheckout(invoiceId); - - // Details - s.Driver.ToggleCollapse("PaymentDetails"); - var details = s.Driver.FindElement(By.CssSelector(".payment-details")); - Assert.Contains("Total Price", details.Text); - Assert.Contains("Total Fiat", details.Text); - Assert.Contains("Exchange Rate", details.Text); - Assert.Contains("Amount Due", details.Text); - Assert.Contains("Recommended Fee", details.Text); - Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); - Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); - Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); - Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); - - // Pay partial amount - await Task.Delay(200); - s.Driver.FindElement(By.Id("test-payment-amount")).Clear(); - s.Driver.FindElement(By.Id("test-payment-amount")).SendKeys("0.00001"); - - // Fake Pay - TestUtils.Eventually(() => - { - s.Driver.FindElement(By.Id("FakePayment")).Click(); - s.Driver.FindElement(By.Id("mine-block")).Click(); - var paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); - Assert.Contains("The invoice hasn't been paid in full", paymentInfo.Text); - Assert.Contains("Please send", paymentInfo.Text); - }); - - s.Driver.Navigate().Refresh(); - - // Pay full amount - s.PayInvoice(); - - // Processing - TestUtils.Eventually(() => - { - var processingSection = s.Driver.WaitForElement(By.Id("processing")); - Assert.True(processingSection.Displayed); - Assert.Contains("Payment Received", processingSection.Text); - Assert.Contains("Your payment has been received and is now processing", processingSection.Text); - }); - s.Driver.FindElement(By.Id("confetti")); - - // Mine - s.MineBlockOnInvoiceCheckout(); - TestUtils.Eventually(() => - { - Assert.Contains("Mined 1 block", - s.Driver.WaitForElement(By.Id("CheatSuccessMessage")).Text); - }); - - // Settled - TestUtils.Eventually(() => - { - var settledSection = s.Driver.WaitForElement(By.Id("settled")); - Assert.True(settledSection.Displayed); - Assert.Contains("Invoice Paid", settledSection.Text); - }); - s.Driver.FindElement(By.Id("confetti")); - s.Driver.FindElement(By.Id("ReceiptLink")); - Assert.True(s.Driver.ElementDoesNotExist(By.Id("ContactLink"))); - Assert.Equal(storeUrl, s.Driver.FindElement(By.Id("StoreLink")).GetAttribute("href")); - - // BIP21 - s.GoToHome(); - s.GoToStore(StoreNavPages.CheckoutAppearance); - s.Driver.SetCheckbox(By.Id("OnChainWithLnInvoiceFallback"), true); - s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), false); - s.Driver.FindElement(By.Id("Save")).Click(); - Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); - - invoiceId = s.CreateInvoice(); - s.GoToInvoiceCheckout(invoiceId); - Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method"))); - Assert.Contains("BTC", s.Driver.FindElement(By.Id("AmountDue")).Text); - qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value"); - clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard"); - payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); - var copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC-CHAIN .truncate-center-start")).Text; - var copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC-CHAIN .truncate-center-start")).Text; - Assert.StartsWith($"bitcoin:{copyAddressOnchain}?amount=", payUrl); - Assert.Contains("?amount=", payUrl); - Assert.Contains("&lightning=", payUrl); - Assert.StartsWith("bcrt", copyAddressOnchain); - Assert.StartsWith("lnbcrt", copyAddressLightning); - Assert.StartsWith($"bitcoin:{copyAddressOnchain.ToUpperInvariant()}?amount=", qrValue); - Assert.Contains("&lightning=LNBCRT", qrValue); - Assert.Contains("&lightning=lnbcrt", clipboard); - Assert.Equal(clipboard, payUrl); - - // Check details - s.Driver.ToggleCollapse("PaymentDetails"); - Assert.Contains("1 BTC = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); - Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); - Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); - Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); - Assert.Contains("BTC", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); - - // Switch to amount displayed in sats - s.GoToHome(); - s.GoToStore(StoreNavPages.CheckoutAppearance); - s.Driver.SetCheckbox(By.Id("LightningAmountInSatoshi"), true); - s.Driver.FindElement(By.Id("Save")).Click(); - Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); - s.GoToInvoiceCheckout(invoiceId); - Assert.Contains("sats", s.Driver.FindElement(By.Id("AmountDue")).Text); - - // Check details - s.Driver.ToggleCollapse("PaymentDetails"); - Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); - Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); - Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); - Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); - Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); - - // BIP21 with LN as default payment method - s.GoToHome(); - invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC-LN"); - s.GoToInvoiceCheckout(invoiceId); - Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method"))); - payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); - Assert.StartsWith("bitcoin:", payUrl); - Assert.Contains("&lightning=lnbcrt", payUrl); - - // Check details - s.Driver.ToggleCollapse("PaymentDetails"); - Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); - Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); - Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-TotalFiat")).Text); - Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-AmountDue")).Text); - Assert.Contains("sats", s.Driver.FindElement(By.Id("PaymentDetails-TotalPrice")).Text); - - // Ensure LNURL is enabled - s.GoToHome(); - s.GoToLightningSettings(); - Assert.True(s.Driver.FindElement(By.Id("LNURLEnabled")).Selected); - - // BIP21 with top-up invoice - invoiceId = s.CreateInvoice(amount: null); - s.GoToInvoiceCheckout(invoiceId); - Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method"))); - qrValue = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-qr-value"); - clipboard = s.Driver.FindElement(By.CssSelector(".qr-container")).GetAttribute("data-clipboard"); - payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); - copyAddressOnchain = s.Driver.FindElement(By.CssSelector("#Address_BTC-CHAIN .truncate-center-start")).Text; - copyAddressLightning = s.Driver.FindElement(By.CssSelector("#Lightning_BTC-CHAIN .truncate-center-start")).Text; - Assert.StartsWith($"bitcoin:{copyAddressOnchain}", payUrl); - Assert.Contains("?lightning=lnurl", payUrl); - Assert.DoesNotContain("amount=", payUrl); - Assert.StartsWith("bcrt", copyAddressOnchain); - Assert.StartsWith("lnurl", copyAddressLightning); - Assert.StartsWith($"bitcoin:{copyAddressOnchain.ToUpperInvariant()}?lightning=LNURL", qrValue); - Assert.Contains($"bitcoin:{copyAddressOnchain}?lightning=lnurl", clipboard); - Assert.Equal(clipboard, payUrl); - - // Check details - s.Driver.ToggleCollapse("PaymentDetails"); - Assert.Contains("1 sat = ", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); - Assert.Contains("$", s.Driver.FindElement(By.Id("PaymentDetails-ExchangeRate")).Text); - s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalFiat")); - s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-AmountDue")); - s.Driver.ElementDoesNotExist(By.Id("PaymentDetails-TotalPrice")); - - // Expiry message should not show amount for top-up invoice - expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); - expirySeconds.Clear(); - expirySeconds.SendKeys("5"); - s.Driver.FindElement(By.Id("Expire")).Click(); - TestUtils.Eventually(() => - { - var paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); - Assert.Contains("This invoice will expire in", paymentInfo.Text); - Assert.Contains("00:0", paymentInfo.Text); - Assert.DoesNotContain("Please send", paymentInfo.Text); - }); - - // Configure countdown timer - s.GoToHome(); - invoiceId = s.CreateInvoice(); - s.GoToHome(); - s.GoToStore(StoreNavPages.CheckoutAppearance); - var displayExpirationTimer = s.Driver.FindElement(By.Id("DisplayExpirationTimer")); - Assert.Equal("5", displayExpirationTimer.GetAttribute("value")); - displayExpirationTimer.Clear(); - displayExpirationTimer.SendKeys("10"); - s.Driver.FindElement(By.Id("Save")).Click(); - Assert.Contains("Store successfully updated", s.FindAlertMessage().Text); - - s.GoToInvoiceCheckout(invoiceId); - var paymentInfo = s.Driver.FindElement(By.Id("PaymentInfo")); - Assert.False(paymentInfo.Displayed); - Assert.DoesNotContain("This invoice will expire in", paymentInfo.Text); - - expirySeconds = s.Driver.FindElement(By.Id("ExpirySeconds")); - expirySeconds.Clear(); - expirySeconds.SendKeys("599"); - s.Driver.FindElement(By.Id("Expire")).Click(); - TestUtils.Eventually(() => - { - paymentInfo = s.Driver.WaitForElement(By.Id("PaymentInfo")); - Assert.True(paymentInfo.Displayed); - Assert.Contains("This invoice will expire in", paymentInfo.Text); - Assert.Contains("09:5", paymentInfo.Text); - }); - - // Disable LNURL again - s.GoToHome(); - s.GoToLightningSettings(); - s.Driver.SetCheckbox(By.Id("LNURLEnabled"), false); - s.Driver.ScrollTo(By.Id("save")); - s.Driver.FindElement(By.Id("save")).Click(); - Assert.Contains("BTC Lightning settings successfully updated", s.FindAlertMessage().Text); - - // Test: - // - NFC/LNURL-W available with just Lightning - // - BIP21 works correctly even though Lightning is default payment method - s.GoToHome(); - invoiceId = s.CreateInvoice(defaultPaymentMethod: "BTC-LN"); - s.GoToInvoiceCheckout(invoiceId); - Assert.Empty(s.Driver.FindElements(By.CssSelector(".payment-method"))); - payUrl = s.Driver.FindElement(By.Id("PayInWallet")).GetAttribute("href"); - Assert.StartsWith("bitcoin:", payUrl); - Assert.Contains("&lightning=lnbcrt", payUrl); - - // Language Switch - var languageSelect = new SelectElement(s.Driver.FindElement(By.Id("DefaultLang"))); - Assert.Equal("English", languageSelect.SelectedOption.Text); - Assert.Equal("View Details", s.Driver.FindElement(By.Id("DetailsToggle")).Text); - Assert.DoesNotContain("lang=", s.Driver.Url); - languageSelect.SelectByText("Deutsch"); - Assert.Equal("Details anzeigen", s.Driver.FindElement(By.Id("DetailsToggle")).Text); - Assert.Contains("lang=de", s.Driver.Url); - - s.Driver.Navigate().Refresh(); - languageSelect = new SelectElement(s.Driver.WaitForElement(By.Id("DefaultLang"))); - Assert.Equal("Deutsch", languageSelect.SelectedOption.Text); - Assert.Equal("Details anzeigen", s.Driver.FindElement(By.Id("DetailsToggle")).Text); - languageSelect.SelectByText("English"); - Assert.Equal("View Details", s.Driver.FindElement(By.Id("DetailsToggle")).Text); - Assert.Contains("lang=en", s.Driver.Url); - } - - [Fact(Timeout = TestTimeout)] - public async Task CanUseCheckoutAsModal() - { - using var s = CreateSeleniumTester(); - await s.StartAsync(); - s.GoToRegister(); - s.RegisterNewUser(); - s.CreateNewStore(); - s.GoToStore(); - s.AddDerivationScheme(); - var invoiceId = s.CreateInvoice(0.001m, "BTC", "a@x.com"); - var invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId); - s.Driver.Navigate() - .GoToUrl(new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}")); - s.Driver.WaitUntilAvailable(By.Name("btcpay")); - - var frameElement = s.Driver.FindElement(By.Name("btcpay")); - Assert.True(frameElement.Displayed); - var iframe = s.Driver.SwitchTo().Frame(frameElement); - iframe.WaitUntilAvailable(By.Id("Checkout")); - - await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(invoice - .GetPaymentPrompt(PaymentTypes.CHAIN.GetPaymentMethodId("BTC")) - .Destination, Network.RegTest), - new Money(0.001m, MoneyUnit.BTC)); - - TestUtils.Eventually(() => - { - var closeButton = iframe.FindElement(By.Id("close")); - Assert.True(closeButton.Displayed); - closeButton.Click(); - }); - s.Driver.AssertElementNotFound(By.Name("btcpay")); - Assert.Equal(s.Driver.Url, - new Uri(s.ServerUri, $"tests/index.html?invoice={invoiceId}").ToString()); - } - } -} diff --git a/BTCPayServer.Tests/PayJoinTests.cs b/BTCPayServer.Tests/PayJoinTests.cs index b03f89977..b3ab8f815 100644 --- a/BTCPayServer.Tests/PayJoinTests.cs +++ b/BTCPayServer.Tests/PayJoinTests.cs @@ -250,11 +250,11 @@ namespace BTCPayServer.Tests await s.StartAsync(); s.RegisterNewUser(true); var receiver = s.CreateNewStore(); - s.GenerateWallet("BTC", "", true, true, ScriptPubKeyType.Segwit); + s.GenerateWallet("BTC", "", true, true); var receiverWalletId = new WalletId(receiver.storeId, "BTC"); var sender = s.CreateNewStore(); - s.GenerateWallet("BTC", "", true, true, ScriptPubKeyType.Segwit); + s.GenerateWallet("BTC", "", true, true); var senderWalletId = new WalletId(sender.storeId, "BTC"); await s.Server.ExplorerNode.GenerateAsync(1); @@ -263,8 +263,7 @@ namespace BTCPayServer.Tests var invoiceId = s.CreateInvoice(receiver.storeId, null, "BTC"); s.GoToInvoiceCheckout(invoiceId); - var bip21 = s.Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn")) - .GetAttribute("href"); + var bip21 = s.Driver.WaitForElement(By.Id("PayInWallet")).GetAttribute("href"); Assert.Contains($"{PayjoinClient.BIP21EndpointKey}=", bip21); s.GoToWallet(senderWalletId, WalletsNavPages.Send); s.Driver.FindElement(By.Id("bip21parse")).Click(); @@ -310,8 +309,7 @@ namespace BTCPayServer.Tests //payjoin is enabled by default. var invoiceId = s.CreateInvoice(receiver.storeId); s.GoToInvoiceCheckout(invoiceId); - var bip21 = s.Driver.WaitForElement(By.ClassName("payment__details__instruction__open-wallet__btn")) - .GetAttribute("href"); + var bip21 = s.Driver.WaitForElement(By.Id("PayInWallet")).GetAttribute("href"); Assert.Contains($"{PayjoinClient.BIP21EndpointKey}=", bip21); s.GoToStore(receiver.storeId); @@ -326,8 +324,7 @@ namespace BTCPayServer.Tests invoiceId = s.CreateInvoice(receiver.storeId); s.GoToInvoiceCheckout(invoiceId); - bip21 = s.Driver.WaitForElement(By.ClassName("payment__details__instruction__open-wallet__btn")) - .GetAttribute("href"); + bip21 = s.Driver.WaitForElement(By.Id("PayInWallet")).GetAttribute("href"); Assert.Contains($"{PayjoinClient.BIP21EndpointKey}=", bip21); s.GoToWallet(senderWalletId, WalletsNavPages.Send); @@ -360,8 +357,7 @@ namespace BTCPayServer.Tests //let's do it all again, except now the receiver has funds and is able to payjoin invoiceId = s.CreateInvoice(); s.GoToInvoiceCheckout(invoiceId); - bip21 = s.Driver.WaitForElement(By.ClassName("payment__details__instruction__open-wallet__btn")) - .GetAttribute("href"); + bip21 = s.Driver.WaitForElement(By.Id("PayInWallet")).GetAttribute("href"); Assert.Contains($"{PayjoinClient.BIP21EndpointKey}", bip21); s.GoToWallet(senderWalletId, WalletsNavPages.Send); @@ -378,7 +374,7 @@ namespace BTCPayServer.Tests s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).Click(); return Task.CompletedTask; }); - s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success); + s.FindAlertMessage(); var handler = s.Server.PayTester.GetService().GetBitcoinHandler("BTC"); await TestUtils.EventuallyAsync(async () => { diff --git a/BTCPayServer.Tests/SeleniumTests.cs b/BTCPayServer.Tests/SeleniumTests.cs index f34eb9d35..39a2b2310 100644 --- a/BTCPayServer.Tests/SeleniumTests.cs +++ b/BTCPayServer.Tests/SeleniumTests.cs @@ -2910,8 +2910,7 @@ namespace BTCPayServer.Tests // Topup Invoice test var i = s.CreateInvoice(storeId, null, cryptoCode); s.GoToInvoiceCheckout(i); - s.Driver.WaitForElement(By.Id("copy-tab")).Click(); - var lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value"); + var lnurl = s.Driver.FindElement(By.CssSelector("#Lightning_BTC-LNURL .truncate-center-start")).Text; var parsed = LNURL.LNURL.Parse(lnurl, out var tag); var fetchedReuqest = Assert.IsType(await LNURL.LNURL.FetchInformation(parsed, new HttpClient())); @@ -2947,11 +2946,9 @@ namespace BTCPayServer.Tests s.GoToStore(storeId); i = s.CreateInvoice(storeId, 0.0000001m, cryptoCode); s.GoToInvoiceCheckout(i); - s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Click(); // BOLT11 is also displayed for standard invoice (not LNURL, even if it is available) - s.Driver.WaitForElement(By.Id("copy-tab")).Click(); - var bolt11 = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value"); - Lightning.BOLT11PaymentRequest.Parse(bolt11, s.Server.ExplorerNode.Network); + var bolt11 = s.Driver.FindElement(By.CssSelector("#Lightning_BTC-LN .truncate-center-start")).Text; + BOLT11PaymentRequest.Parse(bolt11, s.Server.ExplorerNode.Network); var invoiceId = s.Driver.Url.Split('/').Last(); using (var resp = await s.Server.PayTester.HttpClient.GetAsync("BTC/lnurl/pay/i/" + invoiceId)) { @@ -2977,7 +2974,7 @@ namespace BTCPayServer.Tests network, new HttpClient()); lnurlResponse2 = await fetchedReuqest.SendRequest(new LightMoney(0.0000001m, LightMoneyUnit.BTC), network, new HttpClient()); - //invoice amounts do no change so the paymnet request is not regenerated + //invoice amounts do no change so the payment request is not regenerated Assert.Equal(lnurlResponse.Pr, lnurlResponse2.Pr); await s.Server.CustomerLightningD.Pay(lnurlResponse.Pr); Assert.Equal(new LightMoney(0.0000001m, LightMoneyUnit.BTC), @@ -2986,12 +2983,10 @@ namespace BTCPayServer.Tests i = s.CreateInvoice(storeId, 0.000001m, cryptoCode); s.GoToInvoiceCheckout(i); - s.Driver.FindElement(By.ClassName("payment__currencies_noborder")); s.GoToStore(storeId); i = s.CreateInvoice(storeId, null, cryptoCode); s.GoToInvoiceCheckout(i); - s.Driver.FindElement(By.ClassName("payment__currencies_noborder")); s.GoToHome(); s.GoToLightningSettings(); @@ -3005,9 +3000,7 @@ namespace BTCPayServer.Tests i = s.CreateInvoice(storeId, null, cryptoCode); s.GoToInvoiceCheckout(i); - s.Driver.FindElement(By.ClassName("payment__currencies_noborder")); - s.Driver.WaitForElement(By.Id("copy-tab")).Click(); - lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value"); + lnurl = s.Driver.FindElement(By.CssSelector("#Lightning_BTC-LNURL .truncate-center-start")).Text; Assert.StartsWith("lnurlp", lnurl); LNURL.LNURL.Parse(lnurl, out tag); @@ -3020,8 +3013,7 @@ namespace BTCPayServer.Tests Assert.Contains($"{cryptoCode} Lightning settings successfully updated", s.FindAlertMessage().Text); var invForPP = s.CreateInvoice(null, cryptoCode); s.GoToInvoiceCheckout(invForPP); - s.Driver.WaitForElement(By.Id("copy-tab")).Click(); - lnurl = s.Driver.FindElement(By.CssSelector("input.checkoutTextbox")).GetAttribute("value"); + lnurl = s.Driver.FindElement(By.CssSelector("#Lightning_BTC-LNURL .truncate-center-start")).Text; LNURL.LNURL.Parse(lnurl, out tag); // Check that pull payment has lightning option