From 2ce0749bb6e77950b069a65296741b2ce77bd575 Mon Sep 17 00:00:00 2001 From: "nicolas.dorier" Date: Wed, 11 Sep 2019 15:36:12 +0900 Subject: [PATCH] Share same browser for all selenium tests --- BTCPayServer.Tests/AuthenticationTests.cs | 186 ++++--- BTCPayServer.Tests/CheckoutUITests.cs | 227 ++++---- BTCPayServer.Tests/SeleniumTester.cs | 39 +- BTCPayServer.Tests/SeleniumTests.cs | 500 ++++++++---------- BTCPayServer.Tests/XUnitCollections.cs | 17 + .../Lightning/LightningLikePaymentHandler.cs | 7 +- 6 files changed, 477 insertions(+), 499 deletions(-) create mode 100644 BTCPayServer.Tests/XUnitCollections.cs diff --git a/BTCPayServer.Tests/AuthenticationTests.cs b/BTCPayServer.Tests/AuthenticationTests.cs index 9b0ace868..834a3e490 100644 --- a/BTCPayServer.Tests/AuthenticationTests.cs +++ b/BTCPayServer.Tests/AuthenticationTests.cs @@ -19,12 +19,16 @@ using OpenQA.Selenium; namespace BTCPayServer.Tests { + [Collection("Selenium collection")] public class AuthenticationTests { - public AuthenticationTests(ITestOutputHelper helper) + public SeleniumTester SeleniumTester { get; } + + public AuthenticationTests(ITestOutputHelper helper, SeleniumTester seleniumTester) { - Logs.Tester = new XUnitLog(helper) {Name = "Tests"}; + Logs.Tester = new XUnitLog(helper) { Name = "Tests" }; Logs.LogProvider = new XUnitLogProvider(helper); + SeleniumTester = seleniumTester; } [Fact] @@ -93,49 +97,45 @@ namespace BTCPayServer.Tests [Fact] public async Task CanUseImplicitFlow() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - var tester = s.Server; + var tester = SeleniumTester.Server; - var user = tester.NewAccount(); - user.GrantAccess(); - var id = Guid.NewGuid().ToString(); - var redirecturi = new Uri("http://127.0.0.1/oidc-callback"); - var openIdClient = await user.RegisterOpenIdClient( - new OpenIddictApplicationDescriptor() - { - ClientId = id, - DisplayName = id, - Permissions = {OpenIddictConstants.Permissions.GrantTypes.Implicit}, - RedirectUris = {redirecturi} - }); - var implicitAuthorizeUrl = new Uri(tester.PayTester.ServerUri, - $"connect/authorize?response_type=token&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid&nonce={Guid.NewGuid().ToString()}"); - s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl); - s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); - s.Driver.FindElement(By.Id("consent-yes")).Click(); - var url = s.Driver.Url; - var results = url.Split("#").Last().Split("&") - .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - await TestApiAgainstAccessToken(results["access_token"], tester, user); - //in Implicit mode, you renew your token by hitting the same endpoint but adding prompt=none. If you are still logged in on the site, you will receive a fresh token. - var implicitAuthorizeUrlSilentModel = new Uri($"{implicitAuthorizeUrl.OriginalString}&prompt=none"); - s.Driver.Navigate().GoToUrl(implicitAuthorizeUrlSilentModel); - url = s.Driver.Url; - results = url.Split("#").Last().Split("&").ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - await TestApiAgainstAccessToken(results["access_token"], tester, user); + var user = tester.NewAccount(); + user.GrantAccess(); + var id = Guid.NewGuid().ToString(); + var redirecturi = new Uri("http://127.0.0.1/oidc-callback"); + var openIdClient = await user.RegisterOpenIdClient( + new OpenIddictApplicationDescriptor() + { + ClientId = id, + DisplayName = id, + Permissions = { OpenIddictConstants.Permissions.GrantTypes.Implicit }, + RedirectUris = { redirecturi } + }); + var implicitAuthorizeUrl = new Uri(tester.PayTester.ServerUri, + $"connect/authorize?response_type=token&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid&nonce={Guid.NewGuid().ToString()}"); + SeleniumTester.Driver.Navigate().GoToUrl(implicitAuthorizeUrl); + SeleniumTester.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); + SeleniumTester.Driver.FindElement(By.Id("consent-yes")).Click(); + var url = SeleniumTester.Driver.Url; + var results = url.Split("#").Last().Split("&") + .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); + await TestApiAgainstAccessToken(results["access_token"], tester, user); + //in Implicit mode, you renew your token by hitting the same endpoint but adding prompt=none. If you are still logged in on the site, you will receive a fresh token. + var implicitAuthorizeUrlSilentModel = new Uri($"{implicitAuthorizeUrl.OriginalString}&prompt=none"); + SeleniumTester.Driver.Navigate().GoToUrl(implicitAuthorizeUrlSilentModel); + url = SeleniumTester.Driver.Url; + results = url.Split("#").Last().Split("&").ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); + await TestApiAgainstAccessToken(results["access_token"], tester, user); - LogoutFlow(tester, id, s); - - s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl); - s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); - - Assert.Throws(() => s.Driver.FindElement(By.Id("consent-yes"))); - results = url.Split("#").Last().Split("&") - .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - await TestApiAgainstAccessToken(results["access_token"], tester, user); - } + LogoutFlow(tester, id, SeleniumTester); + + SeleniumTester.Driver.Navigate().GoToUrl(implicitAuthorizeUrl); + SeleniumTester.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); + + Assert.Throws(() => SeleniumTester.Driver.FindElement(By.Id("consent-yes"))); + results = url.Split("#").Last().Split("&") + .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); + await TestApiAgainstAccessToken(results["access_token"], tester, user); } void LogoutFlow(ServerTester tester, string clientId, SeleniumTester seleniumTester) @@ -145,50 +145,47 @@ namespace BTCPayServer.Tests seleniumTester.Driver.Navigate().GoToUrl(logoutUrl); seleniumTester.GoToHome(); Assert.Throws(() => seleniumTester.Driver.FindElement(By.Id("Logout"))); - + } [Trait("Selenium", "Selenium")] [Fact] public async Task CanUseCodeFlow() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - var tester = s.Server; + var tester = SeleniumTester.Server; - var user = tester.NewAccount(); - user.GrantAccess(); - var id = Guid.NewGuid().ToString(); - var redirecturi = new Uri("http://127.0.0.1/oidc-callback"); - var secret = "secret"; - var openIdClient = await user.RegisterOpenIdClient( - new OpenIddictApplicationDescriptor() + var user = tester.NewAccount(); + user.GrantAccess(); + var id = Guid.NewGuid().ToString(); + var redirecturi = new Uri("http://127.0.0.1/oidc-callback"); + var secret = "secret"; + var openIdClient = await user.RegisterOpenIdClient( + new OpenIddictApplicationDescriptor() + { + ClientId = id, + DisplayName = id, + Permissions = { - ClientId = id, - DisplayName = id, - Permissions = - { OpenIddictConstants.Permissions.GrantTypes.AuthorizationCode, OpenIddictConstants.Permissions.GrantTypes.RefreshToken - }, - RedirectUris = {redirecturi} - }, secret); - var authorizeUrl = new Uri(tester.PayTester.ServerUri, - $"connect/authorize?response_type=code&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid offline_access&state={Guid.NewGuid().ToString()}"); - s.Driver.Navigate().GoToUrl(authorizeUrl); - s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); - s.Driver.FindElement(By.Id("consent-yes")).Click(); - var url = s.Driver.Url; - var results = url.Split("?").Last().Split("&") - .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); + }, + RedirectUris = { redirecturi } + }, secret); + var authorizeUrl = new Uri(tester.PayTester.ServerUri, + $"connect/authorize?response_type=code&client_id={id}&redirect_uri={redirecturi.AbsoluteUri}&scope=openid offline_access&state={Guid.NewGuid().ToString()}"); + SeleniumTester.Driver.Navigate().GoToUrl(authorizeUrl); + SeleniumTester.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); + SeleniumTester.Driver.FindElement(By.Id("consent-yes")).Click(); + var url = SeleniumTester.Driver.Url; + var results = url.Split("?").Last().Split("&") + .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - var httpClient = tester.PayTester.HttpClient; + var httpClient = tester.PayTester.HttpClient; - var httpRequest = new HttpRequestMessage(HttpMethod.Post, - new Uri(tester.PayTester.ServerUri, "/connect/token")) - { - Content = new FormUrlEncodedContent(new List>() + var httpRequest = new HttpRequestMessage(HttpMethod.Post, + new Uri(tester.PayTester.ServerUri, "/connect/token")) + { + Content = new FormUrlEncodedContent(new List>() { new KeyValuePair("grant_type", OpenIddictConstants.GrantTypes.AuthorizationCode), @@ -197,31 +194,30 @@ namespace BTCPayServer.Tests new KeyValuePair("code", results["code"]), new KeyValuePair("redirect_uri", redirecturi.AbsoluteUri) }) - }; + }; - var response = await httpClient.SendAsync(httpRequest); + var response = await httpClient.SendAsync(httpRequest); - Assert.True(response.IsSuccessStatusCode); + Assert.True(response.IsSuccessStatusCode); - string content = await response.Content.ReadAsStringAsync(); - var result = JObject.Parse(content).ToObject(); + string content = await response.Content.ReadAsStringAsync(); + var result = JObject.Parse(content).ToObject(); - await TestApiAgainstAccessToken(result.AccessToken, tester, user); + await TestApiAgainstAccessToken(result.AccessToken, tester, user); - var refreshedAccessToken = await RefreshAnAccessToken(result.RefreshToken, httpClient, id, secret); + var refreshedAccessToken = await RefreshAnAccessToken(result.RefreshToken, httpClient, id, secret); - await TestApiAgainstAccessToken(refreshedAccessToken, tester, user); - - LogoutFlow(tester, id, s); - s.Driver.Navigate().GoToUrl(authorizeUrl); - s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); - - Assert.Throws(() => s.Driver.FindElement(By.Id("consent-yes"))); - results = url.Split("?").Last().Split("&") - .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); - Assert.True(results.ContainsKey("code")); - } + await TestApiAgainstAccessToken(refreshedAccessToken, tester, user); + + LogoutFlow(tester, id, SeleniumTester); + SeleniumTester.Driver.Navigate().GoToUrl(authorizeUrl); + SeleniumTester.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); + + Assert.Throws(() => SeleniumTester.Driver.FindElement(By.Id("consent-yes"))); + results = url.Split("?").Last().Split("&") + .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); + Assert.True(results.ContainsKey("code")); } private static async Task RefreshAnAccessToken(string refreshToken, HttpClient client, string clientId, @@ -261,7 +257,7 @@ namespace BTCPayServer.Tests { ClientId = id, DisplayName = id, - Permissions = {OpenIddictConstants.Permissions.GrantTypes.ClientCredentials} + Permissions = { OpenIddictConstants.Permissions.GrantTypes.ClientCredentials } }, secret); @@ -300,7 +296,7 @@ namespace BTCPayServer.Tests { ClientId = id, DisplayName = id, - Permissions = {OpenIddictConstants.Permissions.GrantTypes.Password} + Permissions = { OpenIddictConstants.Permissions.GrantTypes.Password } }, secret); diff --git a/BTCPayServer.Tests/CheckoutUITests.cs b/BTCPayServer.Tests/CheckoutUITests.cs index 8d0dd0d5f..1a7cfa227 100644 --- a/BTCPayServer.Tests/CheckoutUITests.cs +++ b/BTCPayServer.Tests/CheckoutUITests.cs @@ -15,163 +15,140 @@ using Xunit.Abstractions; namespace BTCPayServer.Tests { [Trait("Selenium", "Selenium")] + [Collection("Selenium collection")] public class CheckoutUITests { - public CheckoutUITests(ITestOutputHelper helper) + public SeleniumTester SeleniumTester { get; } + + public CheckoutUITests(ITestOutputHelper helper, SeleniumTester seleniumTester) { - Logs.Tester = new XUnitLog(helper) {Name = "Tests"}; + Logs.Tester = new XUnitLog(helper) { Name = "Tests" }; Logs.LogProvider = new XUnitLogProvider(helper); + SeleniumTester = seleniumTester; } - - + + [Fact] public void CanCreateInvoice() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - s.RegisterNewUser(); - var store = s.CreateNewStore().storeName; - s.AddDerivationScheme(); + SeleniumTester.RegisterNewUser(); + var store = SeleniumTester.CreateNewStore().storeName; + SeleniumTester.AddDerivationScheme(); - s.CreateInvoice(store); + SeleniumTester.CreateInvoice(store); - s.Driver.FindElement(By.ClassName("invoice-details-link")).Click(); - s.Driver.AssertNoError(); - s.Driver.Navigate().Back(); - s.Driver.FindElement(By.ClassName("invoice-checkout-link")).Click(); - Assert.NotEmpty(s.Driver.FindElements(By.Id("checkoutCtrl"))); - s.Driver.Quit(); - } + SeleniumTester.Driver.FindElement(By.ClassName("invoice-details-link")).Click(); + SeleniumTester.Driver.AssertNoError(); + SeleniumTester.Driver.Navigate().Back(); + SeleniumTester.Driver.FindElement(By.ClassName("invoice-checkout-link")).Click(); + Assert.NotEmpty(SeleniumTester.Driver.FindElements(By.Id("checkoutCtrl"))); } - + [Fact] public async Task CanHandleRefundEmailForm() { + SeleniumTester.RegisterNewUser(); + var store = SeleniumTester.CreateNewStore(); + SeleniumTester.AddDerivationScheme("BTC"); - using (var s = SeleniumTester.Create()) - { - s.Start(); - s.RegisterNewUser(); - var store = s.CreateNewStore(); - s.AddDerivationScheme("BTC"); + var emailAlreadyThereInvoiceId = SeleniumTester.CreateInvoice(store.storeName, 100, "USD", "a@g.com"); + SeleniumTester.GoToInvoiceCheckout(emailAlreadyThereInvoiceId); + SeleniumTester.Driver.AssertElementNotFound(By.Id("emailAddressFormInput")); + SeleniumTester.GoToHome(); + var invoiceId = SeleniumTester.CreateInvoice(store.storeName); + SeleniumTester.GoToInvoiceCheckout(invoiceId); + Assert.True(SeleniumTester.Driver.FindElement(By.Id("emailAddressFormInput")).Displayed); + SeleniumTester.Driver.FindElement(By.Id("emailAddressFormInput")).SendKeys("xxx"); + SeleniumTester.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button")) + .Click(); - var emailAlreadyThereInvoiceId =s.CreateInvoice(store.storeName, 100, "USD", "a@g.com"); - s.GoToInvoiceCheckout(emailAlreadyThereInvoiceId); - s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput")); - s.GoToHome(); - var invoiceId = s.CreateInvoice(store.storeName); - s.GoToInvoiceCheckout(invoiceId); - Assert.True(s.Driver.FindElement(By.Id("emailAddressFormInput")).Displayed); - s.Driver.FindElement(By.Id("emailAddressFormInput")).SendKeys("xxx"); - s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button")) - .Click(); - - Assert.True(s.Driver.FindElement(By.Id("emailAddressFormInput")).Displayed); - s.Driver.FindElement(By.Id("emailAddressFormInput")).SendKeys("@g.com"); - s.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button")) - .Click(); + Assert.True(SeleniumTester.Driver.FindElement(By.Id("emailAddressFormInput")).Displayed); + SeleniumTester.Driver.FindElement(By.Id("emailAddressFormInput")).SendKeys("@g.com"); + SeleniumTester.Driver.FindElement(By.Id("emailAddressForm")).FindElement(By.CssSelector("button.action-button")) + .Click(); - await Task.Delay(1000); - s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput")); - - s.Driver.Navigate().Refresh(); + await Task.Delay(1000); + SeleniumTester.Driver.AssertElementNotFound(By.Id("emailAddressFormInput")); - s.Driver.AssertElementNotFound(By.Id("emailAddressFormInput")); - } + SeleniumTester.Driver.Navigate().Refresh(); + + SeleniumTester.Driver.AssertElementNotFound(By.Id("emailAddressFormInput")); } [Fact] public async Task CanUseLanguageDropdown() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - s.RegisterNewUser(); - var store = s.CreateNewStore(); - s.AddDerivationScheme("BTC"); + SeleniumTester.RegisterNewUser(); + var store = SeleniumTester.CreateNewStore(); + SeleniumTester.AddDerivationScheme("BTC"); - var invoiceId = s.CreateInvoice(store.storeName); - 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(); - } + var invoiceId = SeleniumTester.CreateInvoice(store.storeName); + SeleniumTester.GoToInvoiceCheckout(invoiceId); + Assert.True(SeleniumTester.Driver.FindElement(By.Id("DefaultLang")).FindElements(By.TagName("option")).Count > 1); + var payWithTextEnglish = SeleniumTester.Driver.FindElement(By.Id("pay-with-text")).Text; + + var prettyDropdown = SeleniumTester.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, SeleniumTester.Driver.FindElement(By.Id("pay-with-text")).Text); + SeleniumTester.Driver.Navigate().GoToUrl(SeleniumTester.Driver.Url + "?lang=da-DK"); + + Assert.NotEqual(payWithTextEnglish, SeleniumTester.Driver.FindElement(By.Id("pay-with-text")).Text); } - + [Fact] public void CanUsePaymentMethodDropdown() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - s.RegisterNewUser(); - var store = s.CreateNewStore(); - s.AddDerivationScheme("BTC"); - - //check that there is no dropdown since only one payment method is set - var invoiceId = s.CreateInvoice(store.storeName, 10, "USD", "a@g.com"); - s.GoToInvoiceCheckout(invoiceId); - s.Driver.FindElement(By.ClassName("payment__currencies_noborder")); - s.GoToHome(); - s.GoToStore(store.storeId); - s.AddDerivationScheme("LTC"); - s.AddLightningNode("BTC",LightningConnectionType.CLightning); - //there should be three now - invoiceId = s.CreateInvoice(store.storeName, 10, "USD", "a@g.com"); - s.GoToInvoiceCheckout(invoiceId); - var currencyDropdownButton = s.Driver.FindElement(By.ClassName("payment__currencies")); - Assert.Contains("BTC", currencyDropdownButton.Text); - currencyDropdownButton.Click(); - - var elements = s.Driver.FindElement(By.ClassName("vex-content")) - .FindElements(By.ClassName("vexmenuitem")); - Assert.Equal(3, elements.Count); - elements.Single(element => element.Text.Contains("LTC")).Click(); - currencyDropdownButton = s.Driver.FindElement(By.ClassName("payment__currencies")); - Assert.Contains("LTC", currencyDropdownButton.Text); - - elements = s.Driver.FindElement(By.ClassName("vex-content")) - .FindElements(By.ClassName("vexmenuitem")); - - elements.Single(element => element.Text.Contains("Lightning")).Click(); - - Assert.Contains("Lightning", currencyDropdownButton.Text); - - s.Driver.Quit(); - } + SeleniumTester.RegisterNewUser(); + var store = SeleniumTester.CreateNewStore(); + SeleniumTester.AddDerivationScheme("BTC"); + + //check that there is no dropdown since only one payment method is set + var invoiceId = SeleniumTester.CreateInvoice(store.storeName, 10, "USD", "a@g.com"); + SeleniumTester.GoToInvoiceCheckout(invoiceId); + SeleniumTester.Driver.FindElement(By.ClassName("payment__currencies_noborder")); + SeleniumTester.GoToHome(); + SeleniumTester.GoToStore(store.storeId); + SeleniumTester.AddDerivationScheme("LTC"); + SeleniumTester.AddLightningNode("BTC", LightningConnectionType.CLightning); + //there should be three now + invoiceId = SeleniumTester.CreateInvoice(store.storeName, 10, "USD", "a@g.com"); + SeleniumTester.GoToInvoiceCheckout(invoiceId); + var currencyDropdownButton = SeleniumTester.Driver.FindElement(By.ClassName("payment__currencies")); + Assert.Contains("BTC", currencyDropdownButton.Text); + currencyDropdownButton.Click(); + + var elements = SeleniumTester.Driver.FindElement(By.ClassName("vex-content")) + .FindElements(By.ClassName("vexmenuitem")); + Assert.Equal(3, elements.Count); + elements.Single(element => element.Text.Contains("LTC")).Click(); + currencyDropdownButton = SeleniumTester.Driver.FindElement(By.ClassName("payment__currencies")); + Assert.Contains("LTC", currencyDropdownButton.Text); + + elements = SeleniumTester.Driver.FindElement(By.ClassName("vex-content")) + .FindElements(By.ClassName("vexmenuitem")); + + elements.Single(element => element.Text.Contains("Lightning")).Click(); + + Assert.Contains("Lightning", currencyDropdownButton.Text); } - + [Fact] public void CanUseLightningSatsFeature() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - s.RegisterNewUser(); - var store = s.CreateNewStore(); - s.AddInternalLightningNode("BTC"); - s.GoToStore(store.storeId, StoreNavPages.Checkout); - s.SetCheckbox(s, "LightningAmountInSatoshi", true); - var command = s.Driver.FindElement(By.Name("command")); - - command.ForceClick(); - var invoiceId = s.CreateInvoice(store.storeName, 10, "USD", "a@g.com"); - s.GoToInvoiceCheckout(invoiceId); - Assert.Contains("Sats", s.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Text); - - } + SeleniumTester.RegisterNewUser(); + var store = SeleniumTester.CreateNewStore(); + SeleniumTester.AddInternalLightningNode("BTC"); + SeleniumTester.GoToStore(store.storeId, StoreNavPages.Checkout); + SeleniumTester.SetCheckbox(SeleniumTester, "LightningAmountInSatoshi", true); + var command = SeleniumTester.Driver.FindElement(By.Name("command")); + + command.ForceClick(); + var invoiceId = SeleniumTester.CreateInvoice(store.storeName, 10, "USD", "a@g.com"); + SeleniumTester.GoToInvoiceCheckout(invoiceId); + Assert.Contains("Sats", SeleniumTester.Driver.FindElement(By.ClassName("payment__currencies_noborder")).Text); } } } diff --git a/BTCPayServer.Tests/SeleniumTester.cs b/BTCPayServer.Tests/SeleniumTester.cs index 3507b6b9f..d4d442536 100644 --- a/BTCPayServer.Tests/SeleniumTester.cs +++ b/BTCPayServer.Tests/SeleniumTester.cs @@ -27,20 +27,42 @@ namespace BTCPayServer.Tests { public class SeleniumTester : IDisposable { - public IWebDriver Driver { get; set; } - public ServerTester Server { get; set; } + private IWebDriver _Driver; + public IWebDriver Driver + { + get + { + if (_Driver == null) + { + Start(); + } + return _Driver; + } + } + private ServerTester _Server; + public ServerTester Server + { + get + { + if (_Server == null) + { + _Server = ServerTester.Create("Default Scope"); + Start(); + } + return _Server; + } + } public static SeleniumTester Create([CallerMemberNameAttribute] string scope = null) { - var server = ServerTester.Create(scope); return new SeleniumTester() { - Server = server + _Server = ServerTester.Create(scope) }; } - public void Start() + void Start() { Server.Start(); ChromeOptions options = new ChromeOptions(); @@ -56,7 +78,7 @@ namespace BTCPayServer.Tests { options.AddArgument("no-sandbox"); } - Driver = new ChromeDriver(Server.PayTester.InContainer ? "/usr/bin" : Directory.GetCurrentDirectory(), options); + _Driver = new ChromeDriver(Server.PayTester.InContainer ? "/usr/bin" : Directory.GetCurrentDirectory(), options); if (isDebug) { //when running locally, depending on your resolution, the website may go into mobile responsive mode and screw with navigation of tests @@ -77,7 +99,10 @@ namespace BTCPayServer.Tests public string RegisterNewUser(bool isAdmin = false) { - var usr = RandomUtils.GetUInt256().ToString() + "@a.com"; + GoToHome(); + if (Driver.PageSource.Contains("id=\"Logout\"")) + Logout(); + var usr = RandomUtils.GetUInt256().ToString().Substring(20) + "@a.com"; Driver.FindElement(By.Id("Register")).Click(); Driver.FindElement(By.Id("Email")).SendKeys(usr); Driver.FindElement(By.Id("Password")).SendKeys("123456"); diff --git a/BTCPayServer.Tests/SeleniumTests.cs b/BTCPayServer.Tests/SeleniumTests.cs index dddc218ba..0d89c6f44 100644 --- a/BTCPayServer.Tests/SeleniumTests.cs +++ b/BTCPayServer.Tests/SeleniumTests.cs @@ -12,79 +12,72 @@ using System.Threading.Tasks; namespace BTCPayServer.Tests { [Trait("Selenium", "Selenium")] + [Collection("Selenium collection")] public class ChromeTests { - public ChromeTests(ITestOutputHelper helper) + public SeleniumTester SeleniumTester { get; } + + public ChromeTests(ITestOutputHelper helper, SeleniumTester seleniumTester) { Logs.Tester = new XUnitLog(helper) { Name = "Tests" }; Logs.LogProvider = new XUnitLogProvider(helper); + SeleniumTester = seleniumTester; } [Fact] public void CanNavigateServerSettings() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - s.RegisterNewUser(true); - s.Driver.FindElement(By.Id("ServerSettings")).Click(); - s.Driver.AssertNoError(); - s.ClickOnAllSideMenus(); - s.Driver.Quit(); - } + SeleniumTester.RegisterNewUser(true); + SeleniumTester.Driver.FindElement(By.Id("ServerSettings")).Click(); + SeleniumTester.Driver.AssertNoError(); + SeleniumTester.ClickOnAllSideMenus(); } [Fact] public void NewUserLogin() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - //Register & Log Out - var email = s.RegisterNewUser(); - s.Driver.FindElement(By.Id("Logout")).Click(); - s.Driver.AssertNoError(); - s.Driver.FindElement(By.Id("Login")).Click(); - s.Driver.AssertNoError(); + //Register & Log Out + var email = SeleniumTester.RegisterNewUser(); + SeleniumTester.Driver.FindElement(By.Id("Logout")).Click(); + SeleniumTester.Driver.AssertNoError(); + SeleniumTester.Driver.FindElement(By.Id("Login")).Click(); + SeleniumTester.Driver.AssertNoError(); - s.Driver.Navigate().GoToUrl(s.Link("/invoices")); - Assert.Contains("ReturnUrl=%2Finvoices", s.Driver.Url); + SeleniumTester.Driver.Navigate().GoToUrl(SeleniumTester.Link("/invoices")); + Assert.Contains("ReturnUrl=%2Finvoices", SeleniumTester.Driver.Url); - // We should be redirected to login - //Same User Can Log Back In - s.Driver.FindElement(By.Id("Email")).SendKeys(email); - s.Driver.FindElement(By.Id("Password")).SendKeys("123456"); - s.Driver.FindElement(By.Id("LoginButton")).Click(); + // We should be redirected to login + //Same User Can Log Back In + SeleniumTester.Driver.FindElement(By.Id("Email")).SendKeys(email); + SeleniumTester.Driver.FindElement(By.Id("Password")).SendKeys("123456"); + SeleniumTester.Driver.FindElement(By.Id("LoginButton")).Click(); - // We should be redirected to invoice - Assert.EndsWith("/invoices", s.Driver.Url); + // We should be redirected to invoice + Assert.EndsWith("/invoices", SeleniumTester.Driver.Url); - // Should not be able to reach server settings - s.Driver.Navigate().GoToUrl(s.Link("/server/users")); - Assert.Contains("ReturnUrl=%2Fserver%2Fusers", s.Driver.Url); + // Should not be able to reach server settings + SeleniumTester.Driver.Navigate().GoToUrl(SeleniumTester.Link("/server/users")); + Assert.Contains("ReturnUrl=%2Fserver%2Fusers", SeleniumTester.Driver.Url); - //Change Password & Log Out - s.Driver.FindElement(By.Id("MySettings")).Click(); - s.Driver.FindElement(By.Id("ChangePassword")).Click(); - s.Driver.FindElement(By.Id("OldPassword")).SendKeys("123456"); - s.Driver.FindElement(By.Id("NewPassword")).SendKeys("abc???"); - s.Driver.FindElement(By.Id("ConfirmPassword")).SendKeys("abc???"); - s.Driver.FindElement(By.Id("UpdatePassword")).Click(); - s.Driver.FindElement(By.Id("Logout")).Click(); - s.Driver.AssertNoError(); + //Change Password & Log Out + SeleniumTester.Driver.FindElement(By.Id("MySettings")).Click(); + SeleniumTester.Driver.FindElement(By.Id("ChangePassword")).Click(); + SeleniumTester.Driver.FindElement(By.Id("OldPassword")).SendKeys("123456"); + SeleniumTester.Driver.FindElement(By.Id("NewPassword")).SendKeys("abc???"); + SeleniumTester.Driver.FindElement(By.Id("ConfirmPassword")).SendKeys("abc???"); + SeleniumTester.Driver.FindElement(By.Id("UpdatePassword")).Click(); + SeleniumTester.Driver.FindElement(By.Id("Logout")).Click(); + SeleniumTester.Driver.AssertNoError(); - //Log In With New Password - s.Driver.FindElement(By.Id("Login")).Click(); - s.Driver.FindElement(By.Id("Email")).SendKeys(email); - s.Driver.FindElement(By.Id("Password")).SendKeys("abc???"); - s.Driver.FindElement(By.Id("LoginButton")).Click(); - Assert.True(s.Driver.PageSource.Contains("Stores"), "Can't Access Stores"); + //Log In With New Password + SeleniumTester.Driver.FindElement(By.Id("Login")).Click(); + SeleniumTester.Driver.FindElement(By.Id("Email")).SendKeys(email); + SeleniumTester.Driver.FindElement(By.Id("Password")).SendKeys("abc???"); + SeleniumTester.Driver.FindElement(By.Id("LoginButton")).Click(); + Assert.True(SeleniumTester.Driver.PageSource.Contains("Stores"), "Can't Access Stores"); - s.Driver.FindElement(By.Id("MySettings")).Click(); - s.ClickOnAllSideMenus(); - - s.Driver.Quit(); - } + SeleniumTester.Driver.FindElement(By.Id("MySettings")).Click(); + SeleniumTester.ClickOnAllSideMenus(); } static void LogIn(SeleniumTester s, string email) @@ -98,270 +91,239 @@ namespace BTCPayServer.Tests [Fact] public async Task CanUseSSHService() { - using (var s = SeleniumTester.Create()) + var alice = SeleniumTester.RegisterNewUser(isAdmin: true); + SeleniumTester.Driver.Navigate().GoToUrl(SeleniumTester.Link("/server/services")); + Assert.Contains("server/services/ssh", SeleniumTester.Driver.PageSource); + using (var client = await SeleniumTester.Server.PayTester.GetService().SSHSettings.ConnectAsync()) { - s.Start(); - var alice = s.RegisterNewUser(isAdmin: true); - s.Driver.Navigate().GoToUrl(s.Link("/server/services")); - Assert.Contains("server/services/ssh", s.Driver.PageSource); - using (var client = await s.Server.PayTester.GetService().SSHSettings.ConnectAsync()) - { - var result = await client.RunBash("echo hello"); - Assert.Equal(string.Empty, result.Error); - Assert.Equal("hello\n", result.Output); - Assert.Equal(0, result.ExitStatus); - } - s.Driver.Navigate().GoToUrl(s.Link("/server/services/ssh")); - s.Driver.AssertNoError(); + var result = await client.RunBash("echo hello"); + Assert.Equal(string.Empty, result.Error); + Assert.Equal("hello\n", result.Output); + Assert.Equal(0, result.ExitStatus); } + SeleniumTester.Driver.Navigate().GoToUrl(SeleniumTester.Link("/server/services/ssh")); + SeleniumTester.Driver.AssertNoError(); } [Fact] public void CanUseDynamicDns() { - using (var s = SeleniumTester.Create()) + var alice = SeleniumTester.RegisterNewUser(isAdmin: true); + SeleniumTester.Driver.Navigate().GoToUrl(SeleniumTester.Link("/server/services")); + Assert.Contains("Dynamic DNS", SeleniumTester.Driver.PageSource); + + SeleniumTester.Driver.Navigate().GoToUrl(SeleniumTester.Link("/server/services/dynamic-dns")); + SeleniumTester.Driver.AssertNoError(); + if (SeleniumTester.Driver.PageSource.Contains("pouet.hello.com")) { - s.Start(); - var alice = s.RegisterNewUser(isAdmin: true); - s.Driver.Navigate().GoToUrl(s.Link("/server/services")); - Assert.Contains("Dynamic DNS", s.Driver.PageSource); - - s.Driver.Navigate().GoToUrl(s.Link("/server/services/dynamic-dns")); - s.Driver.AssertNoError(); - if (s.Driver.PageSource.Contains("pouet.hello.com")) - { - // Cleanup old test run - s.Driver.Navigate().GoToUrl(s.Link("/server/services/dynamic-dns/pouet.hello.com/delete")); - s.Driver.FindElement(By.Id("continue")).Click(); - } - s.Driver.FindElement(By.Id("AddDynamicDNS")).Click(); - s.Driver.AssertNoError(); - // We will just cheat for test purposes by only querying the server - s.Driver.FindElement(By.Id("ServiceUrl")).SendKeys(s.Link("/")); - s.Driver.FindElement(By.Id("Settings_Hostname")).SendKeys("pouet.hello.com"); - s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("MyLog"); - s.Driver.FindElement(By.Id("Settings_Password")).SendKeys("MyLog" + Keys.Enter); - s.Driver.AssertNoError(); - Assert.Contains("The Dynamic DNS has been successfully queried", s.Driver.PageSource); - Assert.EndsWith("/server/services/dynamic-dns", s.Driver.Url); - - // Try to do the same thing should fail (hostname already exists) - s.Driver.FindElement(By.Id("AddDynamicDNS")).Click(); - s.Driver.AssertNoError(); - s.Driver.FindElement(By.Id("ServiceUrl")).SendKeys(s.Link("/")); - s.Driver.FindElement(By.Id("Settings_Hostname")).SendKeys("pouet.hello.com"); - s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("MyLog"); - s.Driver.FindElement(By.Id("Settings_Password")).SendKeys("MyLog" + Keys.Enter); - s.Driver.AssertNoError(); - Assert.Contains("This hostname already exists", s.Driver.PageSource); - - // Delete it - s.Driver.Navigate().GoToUrl(s.Link("/server/services/dynamic-dns")); - Assert.Contains("/server/services/dynamic-dns/pouet.hello.com/delete", s.Driver.PageSource); - s.Driver.Navigate().GoToUrl(s.Link("/server/services/dynamic-dns/pouet.hello.com/delete")); - s.Driver.FindElement(By.Id("continue")).Click(); - s.Driver.AssertNoError(); - - Assert.DoesNotContain("/server/services/dynamic-dns/pouet.hello.com/delete", s.Driver.PageSource); + // Cleanup old test run + SeleniumTester.Driver.Navigate().GoToUrl(SeleniumTester.Link("/server/services/dynamic-dns/pouet.hello.com/delete")); + SeleniumTester.Driver.FindElement(By.Id("continue")).Click(); } + SeleniumTester.Driver.FindElement(By.Id("AddDynamicDNS")).Click(); + SeleniumTester.Driver.AssertNoError(); + // We will just cheat for test purposes by only querying the server + SeleniumTester.Driver.FindElement(By.Id("ServiceUrl")).SendKeys(SeleniumTester.Link("/")); + SeleniumTester.Driver.FindElement(By.Id("Settings_Hostname")).SendKeys("pouet.hello.com"); + SeleniumTester.Driver.FindElement(By.Id("Settings_Login")).SendKeys("MyLog"); + SeleniumTester.Driver.FindElement(By.Id("Settings_Password")).SendKeys("MyLog" + Keys.Enter); + SeleniumTester.Driver.AssertNoError(); + Assert.Contains("The Dynamic DNS has been successfully queried", SeleniumTester.Driver.PageSource); + Assert.EndsWith("/server/services/dynamic-dns", SeleniumTester.Driver.Url); + + // Try to do the same thing should fail (hostname already exists) + SeleniumTester.Driver.FindElement(By.Id("AddDynamicDNS")).Click(); + SeleniumTester.Driver.AssertNoError(); + SeleniumTester.Driver.FindElement(By.Id("ServiceUrl")).SendKeys(SeleniumTester.Link("/")); + SeleniumTester.Driver.FindElement(By.Id("Settings_Hostname")).SendKeys("pouet.hello.com"); + SeleniumTester.Driver.FindElement(By.Id("Settings_Login")).SendKeys("MyLog"); + SeleniumTester.Driver.FindElement(By.Id("Settings_Password")).SendKeys("MyLog" + Keys.Enter); + SeleniumTester.Driver.AssertNoError(); + Assert.Contains("This hostname already exists", SeleniumTester.Driver.PageSource); + + // Delete it + SeleniumTester.Driver.Navigate().GoToUrl(SeleniumTester.Link("/server/services/dynamic-dns")); + Assert.Contains("/server/services/dynamic-dns/pouet.hello.com/delete", SeleniumTester.Driver.PageSource); + SeleniumTester.Driver.Navigate().GoToUrl(SeleniumTester.Link("/server/services/dynamic-dns/pouet.hello.com/delete")); + SeleniumTester.Driver.FindElement(By.Id("continue")).Click(); + SeleniumTester.Driver.AssertNoError(); + + Assert.DoesNotContain("/server/services/dynamic-dns/pouet.hello.com/delete", SeleniumTester.Driver.PageSource); } [Fact] public void CanCreateStores() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - var alice = s.RegisterNewUser(); - var store = s.CreateNewStore().storeName; - s.AddDerivationScheme(); - s.Driver.AssertNoError(); - Assert.Contains(store, s.Driver.PageSource); - var storeUrl = s.Driver.Url; - s.ClickOnAllSideMenus(); - s.GoToInvoices(); - s.CreateInvoice(store); - s.Driver.FindElement(By.ClassName("invoice-details-link")).Click(); - var invoiceUrl = s.Driver.Url; + var alice = SeleniumTester.RegisterNewUser(); + var store = SeleniumTester.CreateNewStore().storeName; + SeleniumTester.AddDerivationScheme(); + SeleniumTester.Driver.AssertNoError(); + Assert.Contains(store, SeleniumTester.Driver.PageSource); + var storeUrl = SeleniumTester.Driver.Url; + SeleniumTester.ClickOnAllSideMenus(); + SeleniumTester.GoToInvoices(); + SeleniumTester.CreateInvoice(store); + SeleniumTester.Driver.FindElement(By.ClassName("invoice-details-link")).Click(); + var invoiceUrl = SeleniumTester.Driver.Url; - // When logout we should not be able to access store and invoice details - s.Driver.FindElement(By.Id("Logout")).Click(); - s.Driver.Navigate().GoToUrl(storeUrl); - Assert.Contains("ReturnUrl", s.Driver.Url); - s.Driver.Navigate().GoToUrl(invoiceUrl); - Assert.Contains("ReturnUrl", s.Driver.Url); + // When logout we should not be able to access store and invoice details + SeleniumTester.Driver.FindElement(By.Id("Logout")).Click(); + SeleniumTester.Driver.Navigate().GoToUrl(storeUrl); + Assert.Contains("ReturnUrl", SeleniumTester.Driver.Url); + SeleniumTester.Driver.Navigate().GoToUrl(invoiceUrl); + Assert.Contains("ReturnUrl", SeleniumTester.Driver.Url); - // When logged we should not be able to access store and invoice details - var bob = s.RegisterNewUser(); - s.Driver.Navigate().GoToUrl(storeUrl); - Assert.Contains("ReturnUrl", s.Driver.Url); - s.Driver.Navigate().GoToUrl(invoiceUrl); - s.AssertNotFound(); - s.GoToHome(); - s.Logout(); + // When logged we should not be able to access store and invoice details + var bob = SeleniumTester.RegisterNewUser(); + SeleniumTester.Driver.Navigate().GoToUrl(storeUrl); + Assert.Contains("ReturnUrl", SeleniumTester.Driver.Url); + SeleniumTester.Driver.Navigate().GoToUrl(invoiceUrl); + SeleniumTester.AssertNotFound(); + SeleniumTester.GoToHome(); + SeleniumTester.Logout(); - // Let's add Bob as a guest to alice's store - LogIn(s, alice); - s.Driver.Navigate().GoToUrl(storeUrl + "/users"); - s.Driver.FindElement(By.Id("Email")).SendKeys(bob + Keys.Enter); - Assert.Contains("User added successfully", s.Driver.PageSource); - s.Logout(); + // Let's add Bob as a guest to alice's store + LogIn(SeleniumTester, alice); + SeleniumTester.Driver.Navigate().GoToUrl(storeUrl + "/users"); + SeleniumTester.Driver.FindElement(By.Id("Email")).SendKeys(bob + Keys.Enter); + Assert.Contains("User added successfully", SeleniumTester.Driver.PageSource); + SeleniumTester.Logout(); - // Bob should not have access to store, but should have access to invoice - LogIn(s, bob); - s.Driver.Navigate().GoToUrl(storeUrl); - Assert.Contains("ReturnUrl", s.Driver.Url); - s.Driver.Navigate().GoToUrl(invoiceUrl); - s.Driver.AssertNoError(); - } + // Bob should not have access to store, but should have access to invoice + LogIn(SeleniumTester, bob); + SeleniumTester.Driver.Navigate().GoToUrl(storeUrl); + Assert.Contains("ReturnUrl", SeleniumTester.Driver.Url); + SeleniumTester.Driver.Navigate().GoToUrl(invoiceUrl); + SeleniumTester.Driver.AssertNoError(); } - + [Fact] public void CanCreateAppPoS() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - s.RegisterNewUser(); - var store = s.CreateNewStore(); + SeleniumTester.RegisterNewUser(); + var store = SeleniumTester.CreateNewStore(); - s.Driver.FindElement(By.Id("Apps")).Click(); - s.Driver.FindElement(By.Id("CreateNewApp")).Click(); - s.Driver.FindElement(By.Name("Name")).SendKeys("PoS" + Guid.NewGuid()); - s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("PointOfSale" + Keys.Enter); - s.Driver.FindElement(By.Id("SelectedStore")).SendKeys(store + Keys.Enter); - s.Driver.FindElement(By.Id("Create")).Click(); - s.Driver.FindElement(By.Id("EnableShoppingCart")).Click(); - s.Driver.FindElement(By.Id("SaveSettings")).ForceClick(); - s.Driver.FindElement(By.Id("ViewApp")).ForceClick(); - s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last()); - Assert.True(s.Driver.PageSource.Contains("Tea shop"), "Unable to create PoS"); - s.Driver.Quit(); - } + SeleniumTester.Driver.FindElement(By.Id("Apps")).Click(); + SeleniumTester.Driver.FindElement(By.Id("CreateNewApp")).Click(); + SeleniumTester.Driver.FindElement(By.Name("Name")).SendKeys("PoS" + Guid.NewGuid()); + SeleniumTester.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("PointOfSale" + Keys.Enter); + SeleniumTester.Driver.FindElement(By.Id("SelectedStore")).SendKeys(store + Keys.Enter); + SeleniumTester.Driver.FindElement(By.Id("Create")).Click(); + SeleniumTester.Driver.FindElement(By.Id("EnableShoppingCart")).Click(); + SeleniumTester.Driver.FindElement(By.Id("SaveSettings")).ForceClick(); + SeleniumTester.Driver.FindElement(By.Id("ViewApp")).ForceClick(); + SeleniumTester.Driver.SwitchTo().Window(SeleniumTester.Driver.WindowHandles.Last()); + Assert.True(SeleniumTester.Driver.PageSource.Contains("Tea shop"), "Unable to create PoS"); } [Fact] public void CanCreateAppCF() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - s.RegisterNewUser(); - var store = s.CreateNewStore(); - s.AddDerivationScheme(); + SeleniumTester.RegisterNewUser(); + var store = SeleniumTester.CreateNewStore(); + SeleniumTester.AddDerivationScheme(); - s.Driver.FindElement(By.Id("Apps")).Click(); - s.Driver.FindElement(By.Id("CreateNewApp")).Click(); - s.Driver.FindElement(By.Name("Name")).SendKeys("CF" + Guid.NewGuid()); - s.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Crowdfund" + Keys.Enter); - s.Driver.FindElement(By.Id("SelectedStore")).SendKeys(store + Keys.Enter); - s.Driver.FindElement(By.Id("Create")).Click(); - s.Driver.FindElement(By.Id("Title")).SendKeys("Kukkstarter"); - s.Driver.FindElement(By.CssSelector("div.note-editable.card-block")).SendKeys("1BTC = 1BTC"); - s.Driver.FindElement(By.Id("TargetCurrency")).SendKeys("JPY"); - s.Driver.FindElement(By.Id("TargetAmount")).SendKeys("700"); - s.Driver.FindElement(By.Id("SaveSettings")).ForceClick(); - s.Driver.FindElement(By.Id("ViewApp")).ForceClick(); - s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last()); - Assert.True(s.Driver.PageSource.Contains("Currently Active!"), "Unable to create CF"); - s.Driver.Quit(); - } + SeleniumTester.Driver.FindElement(By.Id("Apps")).Click(); + SeleniumTester.Driver.FindElement(By.Id("CreateNewApp")).Click(); + SeleniumTester.Driver.FindElement(By.Name("Name")).SendKeys("CF" + Guid.NewGuid()); + SeleniumTester.Driver.FindElement(By.Id("SelectedAppType")).SendKeys("Crowdfund" + Keys.Enter); + SeleniumTester.Driver.FindElement(By.Id("SelectedStore")).SendKeys(store + Keys.Enter); + SeleniumTester.Driver.FindElement(By.Id("Create")).Click(); + SeleniumTester.Driver.FindElement(By.Id("Title")).SendKeys("Kukkstarter"); + SeleniumTester.Driver.FindElement(By.CssSelector("div.note-editable.card-block")).SendKeys("1BTC = 1BTC"); + SeleniumTester.Driver.FindElement(By.Id("TargetCurrency")).SendKeys("JPY"); + SeleniumTester.Driver.FindElement(By.Id("TargetAmount")).SendKeys("700"); + SeleniumTester.Driver.FindElement(By.Id("SaveSettings")).ForceClick(); + SeleniumTester.Driver.FindElement(By.Id("ViewApp")).ForceClick(); + SeleniumTester.Driver.SwitchTo().Window(SeleniumTester.Driver.WindowHandles.Last()); + Assert.True(SeleniumTester.Driver.PageSource.Contains("Currently Active!"), "Unable to create CF"); } [Fact] public void CanCreatePayRequest() { - using (var s = SeleniumTester.Create()) - { - s.Start(); - s.RegisterNewUser(); - s.CreateNewStore(); - s.AddDerivationScheme(); + SeleniumTester.RegisterNewUser(); + SeleniumTester.CreateNewStore(); + SeleniumTester.AddDerivationScheme(); - s.Driver.FindElement(By.Id("PaymentRequests")).Click(); - s.Driver.FindElement(By.Id("CreatePaymentRequest")).Click(); - s.Driver.FindElement(By.Id("Title")).SendKeys("Pay123"); - s.Driver.FindElement(By.Id("Amount")).SendKeys("700"); - s.Driver.FindElement(By.Id("Currency")).SendKeys("BTC"); - s.Driver.FindElement(By.Id("SaveButton")).ForceClick(); - s.Driver.FindElement(By.Name("ViewAppButton")).SendKeys(Keys.Return); - s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last()); - Assert.True(s.Driver.PageSource.Contains("Amount due"), "Unable to create Payment Request"); - s.Driver.Quit(); - } + SeleniumTester.Driver.FindElement(By.Id("PaymentRequests")).Click(); + SeleniumTester.Driver.FindElement(By.Id("CreatePaymentRequest")).Click(); + SeleniumTester.Driver.FindElement(By.Id("Title")).SendKeys("Pay123"); + SeleniumTester.Driver.FindElement(By.Id("Amount")).SendKeys("700"); + SeleniumTester.Driver.FindElement(By.Id("Currency")).SendKeys("BTC"); + SeleniumTester.Driver.FindElement(By.Id("SaveButton")).ForceClick(); + SeleniumTester.Driver.FindElement(By.Name("ViewAppButton")).SendKeys(Keys.Return); + SeleniumTester.Driver.SwitchTo().Window(SeleniumTester.Driver.WindowHandles.Last()); + Assert.True(SeleniumTester.Driver.PageSource.Contains("Amount due"), "Unable to create Payment Request"); } [Fact] public void CanManageWallet() { - using (var s = SeleniumTester.Create()) + SeleniumTester.RegisterNewUser(); + SeleniumTester.CreateNewStore(); + + // In this test, we try to spend from a manual seed. We import the xpub 49'/0'/0', then try to use the seed + // to sign the transaction + var mnemonic = "usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage"; + var root = new Mnemonic(mnemonic).DeriveExtKey(); + SeleniumTester.AddDerivationScheme("BTC", "ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD"); + var tx = SeleniumTester.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create("bcrt1qmxg8fgnmkp354vhe78j6sr4ut64tyz2xyejel4", Network.RegTest), Money.Coins(3.0m)); + SeleniumTester.Server.ExplorerNode.Generate(1); + + SeleniumTester.Driver.FindElement(By.Id("Wallets")).Click(); + SeleniumTester.Driver.FindElement(By.LinkText("Manage")).Click(); + + SeleniumTester.ClickOnAllSideMenus(); + + // We setup the fingerprint and the account key path + SeleniumTester.Driver.FindElement(By.Id("WalletSettings")).ForceClick(); + SeleniumTester.Driver.FindElement(By.Id("AccountKeys_0__MasterFingerprint")).SendKeys("8bafd160"); + SeleniumTester.Driver.FindElement(By.Id("AccountKeys_0__AccountKeyPath")).SendKeys("m/49'/0'/0'" + Keys.Enter); + + // Check the tx sent earlier arrived + SeleniumTester.Driver.FindElement(By.Id("WalletTransactions")).ForceClick(); + var walletTransactionLink = SeleniumTester.Driver.Url; + Assert.Contains(tx.ToString(), SeleniumTester.Driver.PageSource); + + + void SignWith(string signingSource) { - s.Start(); - s.RegisterNewUser(); - s.CreateNewStore(); + // Send to bob + SeleniumTester.Driver.FindElement(By.Id("WalletSend")).Click(); + var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest); + SetTransactionOutput(0, bob, 1); + SeleniumTester.Driver.ScrollTo(By.Id("SendMenu")); + SeleniumTester.Driver.FindElement(By.Id("SendMenu")).ForceClick(); + SeleniumTester.Driver.FindElement(By.CssSelector("button[value=seed]")).Click(); - // In this test, we try to spend from a manual seed. We import the xpub 49'/0'/0', then try to use the seed - // to sign the transaction - var mnemonic = "usage fever hen zero slide mammal silent heavy donate budget pulse say brain thank sausage brand craft about save attract muffin advance illegal cabbage"; - var root = new Mnemonic(mnemonic).DeriveExtKey(); - s.AddDerivationScheme("BTC", "ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD"); - var tx = s.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create("bcrt1qmxg8fgnmkp354vhe78j6sr4ut64tyz2xyejel4", Network.RegTest), Money.Coins(3.0m)); - s.Server.ExplorerNode.Generate(1); + // Input the seed + SeleniumTester.Driver.FindElement(By.Id("SeedOrKey")).SendKeys(signingSource + Keys.Enter); - s.Driver.FindElement(By.Id("Wallets")).Click(); - s.Driver.FindElement(By.LinkText("Manage")).Click(); - - s.ClickOnAllSideMenus(); - - // We setup the fingerprint and the account key path - s.Driver.FindElement(By.Id("WalletSettings")).ForceClick(); - s.Driver.FindElement(By.Id("AccountKeys_0__MasterFingerprint")).SendKeys("8bafd160"); - s.Driver.FindElement(By.Id("AccountKeys_0__AccountKeyPath")).SendKeys("m/49'/0'/0'" + Keys.Enter); - - // Check the tx sent earlier arrived - s.Driver.FindElement(By.Id("WalletTransactions")).ForceClick(); - var walletTransactionLink = s.Driver.Url; - Assert.Contains(tx.ToString(), s.Driver.PageSource); - - - void SignWith(string signingSource) - { - // Send to bob - s.Driver.FindElement(By.Id("WalletSend")).Click(); - var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest); - SetTransactionOutput(0, bob, 1); - s.Driver.ScrollTo(By.Id("SendMenu")); - s.Driver.FindElement(By.Id("SendMenu")).ForceClick(); - s.Driver.FindElement(By.CssSelector("button[value=seed]")).Click(); - - // Input the seed - s.Driver.FindElement(By.Id("SeedOrKey")).SendKeys(signingSource + Keys.Enter); - - // Broadcast - Assert.Contains(bob.ToString(), s.Driver.PageSource); - Assert.Contains("1.00000000", s.Driver.PageSource); - s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick(); - Assert.Equal(walletTransactionLink, s.Driver.Url); - } - - void SetTransactionOutput(int index, BitcoinAddress dest, decimal amount, bool subtract = false) - { - s.Driver.FindElement(By.Id($"Outputs_{index}__DestinationAddress")).SendKeys(dest.ToString()); - var amountElement = s.Driver.FindElement(By.Id($"Outputs_{index}__Amount")); - amountElement.Clear(); - amountElement.SendKeys(amount.ToString()); - var checkboxElement = s.Driver.FindElement(By.Id($"Outputs_{index}__SubtractFeesFromOutput")); - if (checkboxElement.Selected != subtract) - { - checkboxElement.Click(); - } - } - SignWith(mnemonic); - var accountKey = root.Derive(new KeyPath("m/49'/0'/0'")).GetWif(Network.RegTest).ToString(); - SignWith(accountKey); + // Broadcast + Assert.Contains(bob.ToString(), SeleniumTester.Driver.PageSource); + Assert.Contains("1.00000000", SeleniumTester.Driver.PageSource); + SeleniumTester.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick(); + Assert.Equal(walletTransactionLink, SeleniumTester.Driver.Url); } + + void SetTransactionOutput(int index, BitcoinAddress dest, decimal amount, bool subtract = false) + { + SeleniumTester.Driver.FindElement(By.Id($"Outputs_{index}__DestinationAddress")).SendKeys(dest.ToString()); + var amountElement = SeleniumTester.Driver.FindElement(By.Id($"Outputs_{index}__Amount")); + amountElement.Clear(); + amountElement.SendKeys(amount.ToString()); + var checkboxElement = SeleniumTester.Driver.FindElement(By.Id($"Outputs_{index}__SubtractFeesFromOutput")); + if (checkboxElement.Selected != subtract) + { + checkboxElement.Click(); + } + } + SignWith(mnemonic); + var accountKey = root.Derive(new KeyPath("m/49'/0'/0'")).GetWif(Network.RegTest).ToString(); + SignWith(accountKey); } } } diff --git a/BTCPayServer.Tests/XUnitCollections.cs b/BTCPayServer.Tests/XUnitCollections.cs new file mode 100644 index 000000000..e5e8367e1 --- /dev/null +++ b/BTCPayServer.Tests/XUnitCollections.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Text; +using Xunit; +using Xunit.Sdk; + +namespace BTCPayServer.Tests +{ + [CollectionDefinition("Selenium collection")] + public class SeleniumCollection : ICollectionFixture + { + // This class has no code, and is never created. Its purpose is simply + // to be the place to apply [CollectionDefinition] and all the + // ICollectionFixture<> interfaces. + } +} diff --git a/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs b/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs index 601f6fd91..abd3b5b49 100644 --- a/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs +++ b/BTCPayServer/Payments/Lightning/LightningLikePaymentHandler.cs @@ -13,6 +13,7 @@ using BTCPayServer.Services.Invoices; using BTCPayServer.Services; using BTCPayServer.Services.Rates; using NBitcoin; +using System.Globalization; namespace BTCPayServer.Payments.Lightning { @@ -177,10 +178,10 @@ namespace BTCPayServer.Payments.Lightning if (storeBlob.LightningAmountInSatoshi && model.CryptoCode == "BTC" ) { model.CryptoCode = "Sats"; - model.BtcDue = Money.Parse(model.BtcDue).ToUnit(MoneyUnit.Satoshi).ToString(); - model.BtcPaid = Money.Parse(model.BtcPaid).ToUnit(MoneyUnit.Satoshi).ToString(); + model.BtcDue = Money.Parse(model.BtcDue).ToUnit(MoneyUnit.Satoshi).ToString(CultureInfo.InvariantCulture); + model.BtcPaid = Money.Parse(model.BtcPaid).ToUnit(MoneyUnit.Satoshi).ToString(CultureInfo.InvariantCulture); model.NetworkFee = new Money(model.NetworkFee, MoneyUnit.BTC).ToUnit(MoneyUnit.Satoshi); - model.OrderAmount = Money.Parse(model.OrderAmount).ToUnit(MoneyUnit.Satoshi).ToString(); + model.OrderAmount = Money.Parse(model.OrderAmount).ToUnit(MoneyUnit.Satoshi).ToString(CultureInfo.InvariantCulture); } } public override string GetCryptoImage(PaymentMethodId paymentMethodId)