Refactor tests

Remove the hacky `ScrollTo`, `ForceClick` and `WaitForElement`.
Add the hacky `WaitForPageLoad`.
This commit is contained in:
Dennis Reimann 2021-01-22 17:49:26 +01:00
parent bd6d38b3b0
commit e567f7a80c
No known key found for this signature in database
GPG Key ID: 5009E1797F03F8D0
10 changed files with 301 additions and 271 deletions

View File

@ -479,21 +479,21 @@ namespace BTCPayServer.Tests
//there should be three now
invoiceId = s.CreateInvoice(store.storeName, 10, "USD", "a@g.com");
s.GoToInvoiceCheckout(invoiceId);
var currencyDropdownButton = s.Driver.WaitForElement(By.ClassName("payment__currencies"));
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.WaitForElement(By.ClassName("payment__currencies"));
currencyDropdownButton = s.Driver.FindElement(By.ClassName("payment__currencies"));
Assert.Contains("LTC", currencyDropdownButton.Text);
currencyDropdownButton.Click();
elements = s.Driver.FindElement(By.ClassName("vex-content")).FindElements(By.ClassName("vexmenuitem"));
elements.Single(element => element.Text.Contains("Lightning")).Click();
currencyDropdownButton = s.Driver.WaitForElement(By.ClassName("payment__currencies"));
currencyDropdownButton = s.Driver.FindElement(By.ClassName("payment__currencies"));
Assert.Contains("Lightning", currencyDropdownButton.Text);
s.Driver.Quit();

View File

@ -86,7 +86,7 @@ namespace BTCPayServer.Tests
var invoiceId = s.CreateInvoice(store.storeName, 10);
s.GoToInvoiceCheckout(invoiceId);
var currencyDropdownButton = s.Driver.WaitForElement(By.ClassName("payment__currencies"));
var currencyDropdownButton = s.Driver.FindElement(By.ClassName("payment__currencies"));
Assert.Contains("ETH", currencyDropdownButton.Text);
s.Driver.FindElement(By.Id("copy-tab")).Click();

View File

@ -65,7 +65,7 @@ namespace BTCPayServer.Tests
s.SetCheckbox(s, "btcpay.store.canmodifystoresettings", true);
s.SetCheckbox(s, "btcpay.user.canviewprofile", true);
s.Driver.FindElement(By.Id("Generate")).Click();
var superApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
var superApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
//this api key has access to everything
await TestApiAgainstAccessToken(superApiKey, tester, user, Policies.CanModifyServerSettings, Policies.CanModifyStoreSettings, Policies.CanViewProfile);
@ -74,7 +74,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.SetCheckbox(s, "btcpay.server.canmodifyserversettings", true);
s.Driver.FindElement(By.Id("Generate")).Click();
var serverOnlyApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
var serverOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(serverOnlyApiKey, tester, user,
Policies.CanModifyServerSettings);
@ -82,7 +82,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.SetCheckbox(s, "btcpay.store.canmodifystoresettings", true);
s.Driver.FindElement(By.Id("Generate")).Click();
var allStoreOnlyApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
var allStoreOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(allStoreOnlyApiKey, tester, user,
Policies.CanModifyStoreSettings);
@ -94,13 +94,13 @@ namespace BTCPayServer.Tests
var storeId = option.GetAttribute("value");
option.Click();
s.Driver.FindElement(By.Id("Generate")).Click();
var selectiveStoreApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
var selectiveStoreApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(selectiveStoreApiKey, tester, user,
Permission.Create(Policies.CanModifyStoreSettings, storeId).ToString());
s.Driver.FindElement(By.Id("AddApiKey")).Click();
s.Driver.FindElement(By.Id("Generate")).Click();
var noPermissionsApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
var noPermissionsApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
await TestApiAgainstAccessToken(noPermissionsApiKey, tester, user);
await Assert.ThrowsAnyAsync<HttpRequestException>(async () =>
@ -188,7 +188,7 @@ namespace BTCPayServer.Tests
checkbox.Click();
}
s.Driver.FindElement(By.Id("Generate")).Click();
var allAPIKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text;
var allAPIKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text;
var apikeydata = await TestApiAgainstAccessToken<ApiKeyData>(allAPIKey, $"api/v1/api-keys/current", tester.PayTester.HttpClient);
Assert.Equal(checkedPermissionCount, apikeydata.Permissions.Length);
}

View File

@ -33,7 +33,7 @@ namespace BTCPayServer.Tests
s.AddDerivationScheme("BTC");
s.GoToStore(store.storeId, StoreNavPages.Checkout);
s.Driver.FindElement(By.Id("RequiresRefundEmail")).Click();
s.Driver.FindElement(By.Name("command")).ForceClick();
s.Driver.FindElement(By.Name("command")).Click();
var emailAlreadyThereInvoiceId = s.CreateInvoice(store.storeName, 100, "USD", "a@g.com");
s.GoToInvoiceCheckout(emailAlreadyThereInvoiceId);
@ -116,7 +116,7 @@ namespace BTCPayServer.Tests
s.SetCheckbox(s, "LightningAmountInSatoshi", true);
var command = s.Driver.FindElement(By.Name("command"));
command.ForceClick();
command.Click();
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);

View File

@ -20,41 +20,25 @@ namespace BTCPayServer.Tests
var res = JsonConvert.SerializeObject(o, Formatting.None, jsonSettings);
return res;
}
public static void ScrollTo(this IWebDriver driver, By by)
public static void WaitForPageLoad(this IWebDriver driver)
{
var element = driver.FindElement(by);
}
/// <summary>
/// Sometimes the chrome driver is fucked up and we need some magic to click on the element.
/// </summary>
/// <param name="element"></param>
public static void ForceClick(this IWebElement element)
{
element.SendKeys(Keys.Return);
// Yes, this sucks big time, but it ensures that the JS after it
// does not get executed in the old page context. Bad, I know.
// Thread.Sleep(TimeSpan.FromMilliseconds(250));
//
// new WebDriverWait(driver, SeleniumTester.ImplicitWait)
// .Until(d=>((IJavaScriptExecutor)d).ExecuteScript("return (document.readyState === 'complete' && (typeof(jQuery) === 'undefined' || jQuery.isReady))").Equals(true));
}
/// <summary>
/// Utility method to wait until timeout for element to be present (optionally displayed)
/// </summary>
/// <param name="context">Wait context</param>
/// <param name="by">How we search for element</param>
/// <param name="displayed">Flag to wait for element to be displayed or just present</param>
/// <param name="timeout">How long to wait for element to be present/displayed</param>
/// <returns>Element we were waiting for</returns>
public static IWebElement WaitForElement(this IWebDriver context, By by, bool displayed = true, uint timeout = 10)
public static void LogIn(this SeleniumTester s, string email)
{
var wait = new DefaultWait<IWebDriver>(context);
wait.Timeout = TimeSpan.FromSeconds(timeout);
wait.IgnoreExceptionTypes(typeof(NoSuchElementException));
return wait.Until(ctx =>
{
var elem = ctx.FindElement(by);
if (displayed && !elem.Displayed)
return null;
return elem;
});
s.Driver.FindElement(By.Id("Email")).SendKeys(email);
s.Driver.FindElement(By.Id("Password")).SendKeys("123456");
s.Driver.FindElement(By.Id("LoginButton")).Click();
s.Driver.AssertNoError();
}
public static void AssertNoError(this IWebDriver driver)
{
try

View File

@ -25,6 +25,7 @@ using NBXplorer.DerivationStrategy;
using NBXplorer.Models;
using Newtonsoft.Json.Linq;
using OpenQA.Selenium;
using OpenQA.Selenium.Support.UI;
using Xunit;
using Xunit.Abstractions;
@ -239,6 +240,7 @@ namespace BTCPayServer.Tests
s.SetCheckbox(s, "PayJoinEnabled", true);
s.Driver.FindElement(By.Id("Save")).Click();
Assert.True(s.Driver.FindElement(By.Id("PayJoinEnabled")).Selected);
var sender = s.CreateNewStore();
var senderSeed = s.GenerateWallet("BTC", "", true, true, format);
var senderWalletId = new WalletId(sender.storeId, "BTC");
@ -257,16 +259,21 @@ namespace BTCPayServer.Tests
s.Driver.SwitchTo().Alert().Accept();
Assert.False(string.IsNullOrEmpty(s.Driver.FindElement(By.Id("PayJoinBIP21"))
.GetAttribute("value")));
s.Driver.ScrollTo(By.Id("SendMenu"));
s.Driver.FindElement(By.Id("SendMenu")).ForceClick();
s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
s.Driver.FindElement(By.Id("SendMenu")).Click();
s.Driver.WaitForPageLoad();
var nbxSeedButton = s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]"));
new WebDriverWait(s.Driver, SeleniumTester.ImplicitWait).Until(d=> nbxSeedButton.Enabled);
nbxSeedButton.Click();
s.Driver.WaitForPageLoad();
await s.Server.WaitForEvent<NewOnChainTransactionEvent>(() =>
{
s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).ForceClick();
s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).Click();
return Task.CompletedTask;
});
//no funds in receiver wallet to do payjoin
s.AssertHappyMessage(StatusMessageModel.StatusSeverity.Warning);
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Warning);
await TestUtils.EventuallyAsync(async () =>
{
var invoice = await s.Server.PayTester.GetService<InvoiceRepository>().GetInvoice(invoiceId);
@ -294,15 +301,14 @@ namespace BTCPayServer.Tests
.GetAttribute("value")));
s.Driver.FindElement(By.Id("FeeSatoshiPerByte")).Clear();
s.Driver.FindElement(By.Id("FeeSatoshiPerByte")).SendKeys("2");
s.Driver.ScrollTo(By.Id("SendMenu"));
s.Driver.FindElement(By.Id("SendMenu")).ForceClick();
s.Driver.FindElement(By.Id("SendMenu")).Click();
s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
var txId = await s.Server.WaitForEvent<NewOnChainTransactionEvent>(() =>
{
s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).ForceClick();
s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).Click();
return Task.CompletedTask;
});
s.AssertHappyMessage(StatusMessageModel.StatusSeverity.Success);
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Success);
await TestUtils.EventuallyAsync(async () =>
{
var invoice = await invoiceRepository.GetInvoice(invoiceId);

View File

@ -20,6 +20,7 @@ using NBitcoin;
using OpenQA.Selenium;
using OpenQA.Selenium.Chrome;
using OpenQA.Selenium.Interactions;
using OpenQA.Selenium.Support.UI;
using Xunit;
namespace BTCPayServer.Tests
@ -28,16 +29,10 @@ namespace BTCPayServer.Tests
{
public IWebDriver Driver { get; set; }
public ServerTester Server { get; set; }
public WalletId WalletId { get; set; }
public static SeleniumTester Create([CallerMemberNameAttribute] string scope = null, bool newDb = false)
{
var server = ServerTester.Create(scope, newDb);
return new SeleniumTester()
{
Server = server
};
}
public static SeleniumTester Create([CallerMemberNameAttribute] string scope = null, bool newDb = false) =>
new SeleniumTester { Server = ServerTester.Create(scope, newDb) };
public async Task StartAsync()
{
@ -48,7 +43,6 @@ namespace BTCPayServer.Tests
// this must be first option https://stackoverflow.com/questions/53073411/selenium-webdriverexceptionchrome-failed-to-start-crashed-as-google-chrome-is#comment102570662_53073789
options.AddArgument("no-sandbox");
}
var isDebug = !Server.PayTester.InContainer;
if (!isDebug)
{
@ -70,22 +64,10 @@ namespace BTCPayServer.Tests
Driver.AssertNoError();
}
internal IWebElement AssertHappyMessage(StatusMessageModel.StatusSeverity severity = StatusMessageModel.StatusSeverity.Success)
{
using var cts = new CancellationTokenSource(20_000);
while (!cts.IsCancellationRequested)
{
var result = Driver.FindElements(By.ClassName($"alert-{StatusMessageModel.ToString(severity)}")).Where(el => el.Displayed);
if (result.Any())
return result.First();
Thread.Sleep(100);
}
Logs.Tester.LogInformation(this.Driver.PageSource);
Assert.True(false, $"Should have shown {severity} message");
return null;
}
internal IWebElement FindAlertMessage(StatusMessageModel.StatusSeverity severity = StatusMessageModel.StatusSeverity.Success) =>
Driver.FindElement(By.ClassName($"alert-{StatusMessageModel.ToString(severity)}"));
public static readonly TimeSpan ImplicitWait = TimeSpan.FromSeconds(10);
public static readonly TimeSpan ImplicitWait = TimeSpan.FromSeconds(5);
public string Link(string relativeLink)
{
return Server.PayTester.ServerUri.AbsoluteUri.WithoutEndingSlash() + relativeLink.WithStartingSlash();
@ -93,8 +75,9 @@ namespace BTCPayServer.Tests
public void GoToRegister()
{
Driver.Navigate().GoToUrl(this.Link("/Account/Register"));
Driver.Navigate().GoToUrl(Link("/Account/Register"));
}
public string RegisterNewUser(bool isAdmin = false)
{
var usr = RandomUtils.GetUInt256().ToString().Substring(64 - 20) + "@a.com";
@ -111,30 +94,38 @@ namespace BTCPayServer.Tests
public (string storeName, string storeId) CreateNewStore()
{
var usr = "Store" + RandomUtils.GetUInt64().ToString();
Driver.FindElement(By.Id("Stores")).Click();
Driver.WaitForPageLoad();
Driver.FindElement(By.Id("CreateStore")).Click();
Driver.FindElement(By.Id("Name")).SendKeys(usr);
Driver.WaitForPageLoad();
var name = "Store" + RandomUtils.GetUInt64();
Driver.FindElement(By.Id("Name")).SendKeys(name);
Driver.FindElement(By.Id("Create")).Click();
Driver.WaitForPageLoad();
StoreId = Driver.FindElement(By.Id("Id")).GetAttribute("value");
return (usr, StoreId);
return (name, StoreId);
}
public string StoreId { get; set; }
public Mnemonic GenerateWallet(string cryptoCode = "BTC", string seed = "", bool importkeys = false, bool privkeys = false, ScriptPubKeyType format = ScriptPubKeyType.Segwit)
{
Driver.FindElement(By.Id($"Modify{cryptoCode}")).ForceClick();
Driver.FindElement(By.Id("import-from-btn")).ForceClick();
Driver.FindElement(By.Id("nbxplorergeneratewalletbtn")).ForceClick();
Driver.WaitForElement(By.Id("ExistingMnemonic")).SendKeys(seed);
SetCheckbox(Driver.WaitForElement(By.Id("SavePrivateKeys")), privkeys);
SetCheckbox(Driver.WaitForElement(By.Id("ImportKeysToRPC")), importkeys);
Driver.WaitForElement(By.Id("ScriptPubKeyType")).Click();
Driver.WaitForElement(By.CssSelector($"#ScriptPubKeyType option[value={format}]")).Click();
Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click();
Driver.WaitForPageLoad();
Driver.FindElement(By.Id("import-from-btn")).Click();
Driver.FindElement(By.Id("nbxplorergeneratewalletbtn")).Click();
Driver.FindElement(By.Id("ExistingMnemonic")).SendKeys(seed);
SetCheckbox(Driver.FindElement(By.Id("SavePrivateKeys")), privkeys);
SetCheckbox(Driver.FindElement(By.Id("ImportKeysToRPC")), importkeys);
Driver.FindElement(By.Id("ScriptPubKeyType")).Click();
Driver.FindElement(By.CssSelector($"#ScriptPubKeyType option[value={format}]")).Click();
Logs.Tester.LogInformation("Trying to click btn-generate");
Driver.WaitForElement(By.Id("btn-generate")).ForceClick();
Driver.FindElement(By.Id("btn-generate")).Click();
// Seed backup page
AssertHappyMessage();
FindAlertMessage();
if (string.IsNullOrEmpty(seed))
{
seed = Driver.FindElements(By.Id("recovery-phrase")).First().GetAttribute("data-mnemonic");
@ -146,14 +137,20 @@ namespace BTCPayServer.Tests
WalletId = new WalletId(StoreId, cryptoCode);
return new Mnemonic(seed);
}
public WalletId WalletId { get; set; }
public void AddDerivationScheme(string cryptoCode = "BTC", string derivationScheme = "xpub661MyMwAqRbcGABgHMUXDzPzH1tU7eZaAaJQXhDXsSxsqyQzQeU6kznNfSuAyqAK9UaWSaZaMFdNiY5BCF4zBPAzSnwfUAwUhwttuAKwfRX-[legacy]")
{
Driver.FindElement(By.Id($"Modify{cryptoCode}")).ForceClick();
Driver.FindElement(By.Id($"Modify{cryptoCode}")).Click();
Driver.WaitForPageLoad();
Driver.FindElement(By.ClassName("store-derivation-scheme")).SendKeys(derivationScheme);
Driver.FindElement(By.Id("Continue")).ForceClick();
Driver.FindElement(By.Id("Confirm")).ForceClick();
AssertHappyMessage();
Driver.FindElement(By.Id("Continue")).Click();
Driver.WaitForPageLoad();
var confirmButton = Driver.FindElement(By.Id("Confirm"));
new WebDriverWait(Driver, ImplicitWait).Until(d=>confirmButton.Enabled);
confirmButton.Click();
Driver.WaitForPageLoad();
FindAlertMessage();
}
public void AddLightningNode(string cryptoCode, LightningConnectionType connectionType)
@ -168,16 +165,16 @@ namespace BTCPayServer.Tests
else
throw new NotSupportedException(connectionType.ToString());
Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).ForceClick();
Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).Click();
Driver.FindElement(By.Name($"ConnectionString")).SendKeys(connectionString);
Driver.FindElement(By.Id($"save")).ForceClick();
Driver.FindElement(By.Id($"save")).Click();
}
public void AddInternalLightningNode(string cryptoCode)
{
Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).ForceClick();
Driver.FindElement(By.Id($"internal-ln-node-setter")).ForceClick();
Driver.FindElement(By.Id($"save")).ForceClick();
Driver.FindElement(By.Id($"Modify-Lightning{cryptoCode}")).Click();
Driver.FindElement(By.Id($"internal-ln-node-setter")).Click();
Driver.FindElement(By.Id($"save")).Click();
}
public void ClickOnAllSideMenus()
@ -189,19 +186,18 @@ namespace BTCPayServer.Tests
{
Logs.Tester.LogInformation($"Checking no error on {l}");
Driver.Navigate().GoToUrl(l);
Driver.WaitForPageLoad();
Driver.AssertNoError();
}
}
public void Dispose()
{
if (Driver != null)
{
try
{
Driver.Close();
Driver.Quit();
}
catch { }
Driver.Dispose();
@ -240,17 +236,24 @@ namespace BTCPayServer.Tests
public void GoToStore(string storeId, StoreNavPages storeNavPage = StoreNavPages.Index)
{
Driver.FindElement(By.Id("Stores")).Click();
Driver.WaitForPageLoad();
Driver.FindElement(By.Id($"update-store-{storeId}")).Click();
Driver.WaitForPageLoad();
if (storeNavPage != StoreNavPages.Index)
{
Driver.FindElement(By.Id(storeNavPage.ToString())).Click();
Driver.WaitForPageLoad();
}
}
public void GoToInvoiceCheckout(string invoiceId)
{
Driver.FindElement(By.Id("Invoices")).Click();
Driver.WaitForPageLoad();
Driver.FindElement(By.Id($"invoice-checkout-{invoiceId}")).Click();
Driver.WaitForPageLoad();
CheckForJSErrors();
}
@ -271,14 +274,7 @@ namespace BTCPayServer.Tests
public void SetCheckbox(SeleniumTester s, string checkboxId, bool value)
{
SetCheckbox(s.Driver.WaitForElement(By.Id(checkboxId)), value);
}
public void ScrollToElement(IWebElement element)
{
Actions actions = new Actions(Driver);
actions.MoveToElement(element);
actions.Perform();
SetCheckbox(s.Driver.FindElement(By.Id(checkboxId)), value);
}
public void GoToInvoices()
@ -300,16 +296,11 @@ namespace BTCPayServer.Tests
Driver.Navigate().GoToUrl(new Uri(Server.PayTester.ServerUri, "Account/Login"));
}
public void GoToCreateInvoicePage()
{
GoToInvoices();
Driver.FindElement(By.Id("CreateNewInvoice")).Click();
}
public string CreateInvoice(string storeName, decimal amount = 100, string currency = "USD", string refundEmail = "")
{
GoToInvoices();
Driver.FindElement(By.Id("CreateNewInvoice")).Click(); // ocassionally gets stuck for some reason, tried force click and wait for element
Driver.FindElement(By.Id("CreateNewInvoice")).Click();
Driver.WaitForPageLoad();
Driver.FindElement(By.Id("Amount")).SendKeys(amount.ToString(CultureInfo.InvariantCulture));
var currencyEl = Driver.FindElement(By.Id("Currency"));
currencyEl.Clear();
@ -318,7 +309,7 @@ namespace BTCPayServer.Tests
Driver.FindElement(By.Name("StoreId")).SendKeys(storeName);
Driver.FindElement(By.Id("Create")).Click();
AssertHappyMessage();
FindAlertMessage();
var statusElement = Driver.FindElement(By.ClassName("alert-success"));
var id = statusElement.Text.Split(" ")[1];
return id;
@ -331,7 +322,7 @@ namespace BTCPayServer.Tests
Driver.FindElement(By.Id("generateButton")).Click();
var addressStr = Driver.FindElement(By.Id("address")).GetProperty("value");
var address = BitcoinAddress.Create(addressStr, ((BTCPayNetwork)Server.NetworkProvider.GetNetwork(walletId.CryptoCode)).NBitcoinNetwork);
for (int i = 0; i < coins; i++)
for (var i = 0; i < coins; i++)
{
await Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(denomination));
}
@ -348,15 +339,11 @@ namespace BTCPayServer.Tests
Driver.FindElement(By.Id("bip21parse")).Click();
Driver.SwitchTo().Alert().SendKeys(bip21);
Driver.SwitchTo().Alert().Accept();
Driver.ScrollTo(By.Id("SendMenu"));
Driver.FindElement(By.Id("SendMenu")).ForceClick();
Driver.FindElement(By.Id("SendMenu")).Click();
Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
}
private void CheckForJSErrors()
{
//wait for seleniun update: https://stackoverflow.com/questions/57520296/selenium-webdriver-3-141-0-driver-manage-logs-availablelogtypes-throwing-syste
@ -384,25 +371,29 @@ namespace BTCPayServer.Tests
{
walletId ??= WalletId;
Driver.Navigate().GoToUrl(new Uri(Server.PayTester.ServerUri, $"wallets/{walletId}"));
Driver.WaitForPageLoad();
if (navPages != WalletsNavPages.Transactions)
{
Driver.FindElement(By.Id($"Wallet{navPages}")).Click();
Driver.WaitForPageLoad();
}
}
public void GoToUrl(string relativeUrl)
{
Driver.Navigate().GoToUrl(new Uri(Server.PayTester.ServerUri, relativeUrl));
Driver.WaitForPageLoad();
}
public void GoToServer(ServerNavPages navPages = ServerNavPages.Index)
{
Driver.FindElement(By.Id("ServerSettings")).Click();
Driver.WaitForPageLoad();
if (navPages != ServerNavPages.Index)
{
Driver.FindElement(By.Id($"Server-{navPages}")).Click();
Driver.WaitForPageLoad();
}
}
public void GoToInvoice(string id)

View File

@ -33,7 +33,8 @@ namespace BTCPayServer.Tests
[Trait("Selenium", "Selenium")]
public class ChromeTests
{
public const int TestTimeout = TestUtils.TestTimeout;
private const int TestTimeout = TestUtils.TestTimeout;
public ChromeTests(ITestOutputHelper helper)
{
Logs.Tester = new XUnitLog(helper) { Name = "Tests" };
@ -85,7 +86,7 @@ namespace BTCPayServer.Tests
Assert.Contains(passEl.Text, "hellorockstar", StringComparison.OrdinalIgnoreCase);
s.Driver.FindElement(By.Id("delete")).Click();
s.Driver.FindElement(By.Id("continue")).Click();
s.AssertHappyMessage();
s.FindAlertMessage();
seedEl = s.Driver.FindElement(By.Id("SeedTextArea"));
Assert.Contains("Seed removed", seedEl.Text, StringComparison.OrdinalIgnoreCase);
}
@ -150,7 +151,7 @@ namespace BTCPayServer.Tests
var usr = RandomUtils.GetUInt256().ToString().Substring(64 - 20) + "@a.com";
s.Driver.FindElement(By.Id("Email")).SendKeys(usr);
s.Driver.FindElement(By.Id("Save")).Click();
var url = s.AssertHappyMessage().FindElement(By.TagName("a")).Text;
var url = s.FindAlertMessage().FindElement(By.TagName("a")).Text;
;
s.Logout();
s.Driver.Navigate().GoToUrl(url);
@ -160,7 +161,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("Password")).SendKeys("123456");
s.Driver.FindElement(By.Id("ConfirmPassword")).SendKeys("123456");
s.Driver.FindElement(By.Id("SetPassword")).Click();
s.AssertHappyMessage();
s.FindAlertMessage();
s.Driver.FindElement(By.Id("Email")).SendKeys(usr);
s.Driver.FindElement(By.Id("Password")).SendKeys("123456");
s.Driver.FindElement(By.Id("LoginButton")).Click();
@ -170,13 +171,6 @@ namespace BTCPayServer.Tests
}
}
static void LogIn(SeleniumTester s, string email)
{
s.Driver.FindElement(By.Id("Email")).SendKeys(email);
s.Driver.FindElement(By.Id("Password")).SendKeys("123456");
s.Driver.FindElement(By.Id("LoginButton")).Click();
s.Driver.AssertNoError();
}
[Fact(Timeout = TestTimeout)]
public async Task CanUseSSHService()
{
@ -185,8 +179,9 @@ namespace BTCPayServer.Tests
await s.StartAsync();
var alice = s.RegisterNewUser(isAdmin: true);
s.Driver.Navigate().GoToUrl(s.Link("/server/services"));
s.Driver.WaitForPageLoad();
Assert.Contains("server/services/ssh", s.Driver.PageSource);
using (var client = await s.Server.PayTester.GetService<BTCPayServer.Configuration.BTCPayServerOptions>().SSHSettings.ConnectAsync())
using (var client = await s.Server.PayTester.GetService<Configuration.BTCPayServerOptions>().SSHSettings.ConnectAsync())
{
var result = await client.RunBash("echo hello");
Assert.Equal(string.Empty, result.Error);
@ -197,7 +192,7 @@ namespace BTCPayServer.Tests
s.Driver.AssertNoError();
s.Driver.FindElement(By.Id("SSHKeyFileContent")).Clear();
s.Driver.FindElement(By.Id("SSHKeyFileContent")).SendKeys("tes't\r\ntest2");
s.Driver.FindElement(By.Id("submit")).ForceClick();
s.Driver.FindElement(By.Id("submit")).Click();
s.Driver.AssertNoError();
var text = s.Driver.FindElement(By.Id("SSHKeyFileContent")).Text;
@ -207,7 +202,7 @@ namespace BTCPayServer.Tests
Assert.True(s.Driver.PageSource.Contains("authorized_keys has been updated", StringComparison.OrdinalIgnoreCase));
s.Driver.FindElement(By.Id("SSHKeyFileContent")).Clear();
s.Driver.FindElement(By.Id("submit")).ForceClick();
s.Driver.FindElement(By.Id("submit")).Click();
text = s.Driver.FindElement(By.Id("SSHKeyFileContent")).Text;
Assert.DoesNotContain("test2", text);
@ -225,7 +220,7 @@ namespace BTCPayServer.Tests
if (s.Driver.PageSource.Contains("Configured"))
{
s.Driver.FindElement(By.CssSelector("button[value=\"ResetPassword\"]")).Submit();
s.AssertHappyMessage();
s.FindAlertMessage();
}
CanSetupEmailCore(s);
s.CreateNewStore();
@ -234,26 +229,6 @@ namespace BTCPayServer.Tests
}
}
private static void CanSetupEmailCore(SeleniumTester s)
{
s.Driver.FindElement(By.ClassName("dropdown-toggle")).Click();
s.Driver.FindElement(By.ClassName("dropdown-item")).Click();
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test@gmail.com");
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
s.AssertHappyMessage();
s.Driver.FindElement(By.Id("Settings_Password")).SendKeys("mypassword");
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
Assert.Contains("Configured", s.Driver.PageSource);
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test_fix@gmail.com");
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
Assert.Contains("Configured", s.Driver.PageSource);
Assert.Contains("test_fix", s.Driver.PageSource);
s.Driver.FindElement(By.CssSelector("button[value=\"ResetPassword\"]")).Submit();
s.AssertHappyMessage();
Assert.DoesNotContain("Configured", s.Driver.PageSource);
Assert.Contains("test_fix", s.Driver.PageSource);
}
[Fact(Timeout = TestTimeout)]
public async Task CanUseDynamicDns()
{
@ -290,6 +265,7 @@ namespace BTCPayServer.Tests
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.WaitForPageLoad();
s.Driver.AssertNoError();
Assert.Contains("This hostname already exists", s.Driver.PageSource);
@ -337,22 +313,24 @@ namespace BTCPayServer.Tests
s.ClickOnAllSideMenus();
s.GoToInvoices();
var invoiceId = s.CreateInvoice(storeData.storeName);
s.AssertHappyMessage();
s.FindAlertMessage();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.ClassName("invoice-details-link")).Click();
var invoiceUrl = s.Driver.Url;
//let's test archiving an invoice
Assert.DoesNotContain("Archived", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text);
s.Driver.FindElement(By.Id("btn-archive-toggle")).Click();
s.AssertHappyMessage();
s.FindAlertMessage();
Assert.Contains("Archived", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text);
//check that it no longer appears in list
s.GoToInvoices();
Assert.DoesNotContain(invoiceId, s.Driver.PageSource);
//ok, let's unarchive and see that it shows again
s.Driver.Navigate().GoToUrl(invoiceUrl);
s.Driver.FindElement(By.Id("btn-archive-toggle")).Click();
s.AssertHappyMessage();
s.FindAlertMessage();
Assert.DoesNotContain("Archived", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text);
s.GoToInvoices();
Assert.Contains(invoiceId, s.Driver.PageSource);
@ -374,14 +352,14 @@ namespace BTCPayServer.Tests
s.Logout();
// Let's add Bob as a guest to alice's store
LogIn(s, alice);
s.LogIn(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();
// Bob should not have access to store, but should have access to invoice
LogIn(s, bob);
s.LogIn(bob);
s.Driver.Navigate().GoToUrl(storeUrl);
Assert.Contains("ReturnUrl", s.Driver.Url);
s.Driver.Navigate().GoToUrl(invoiceUrl);
@ -389,8 +367,9 @@ namespace BTCPayServer.Tests
// Alice should be able to delete the store
s.Logout();
LogIn(s, alice);
s.LogIn(alice);
s.Driver.FindElement(By.Id("Stores")).Click();
s.Driver.WaitForPageLoad();
// there shouldn't be any hints now
Assert.False(s.Driver.PageSource.Contains(offchainHint), "Lightning hint should be dismissed at this point");
@ -422,7 +401,7 @@ namespace BTCPayServer.Tests
string pairingCode = AssertUrlHasPairingCode(s);
s.Driver.FindElement(By.Id("ApprovePairing")).Click();
s.AssertHappyMessage();
s.FindAlertMessage();
Assert.Contains(pairingCode, s.Driver.PageSource);
var client = new NBitpayClient.Bitpay(new Key(), s.Server.PayTester.ServerUri);
@ -454,15 +433,6 @@ namespace BTCPayServer.Tests
}
}
private static string AssertUrlHasPairingCode(SeleniumTester s)
{
var regex = Regex.Match(new Uri(s.Driver.Url, UriKind.Absolute).Query, "pairingCode=([^&]*)");
Assert.True(regex.Success, $"{s.Driver.Url} does not match expected regex");
var pairingCode = regex.Groups[1].Value;
return pairingCode;
}
[Fact(Timeout = TestTimeout)]
public async Task CanCreateAppPoS()
{
@ -470,29 +440,31 @@ namespace BTCPayServer.Tests
{
await s.StartAsync();
s.RegisterNewUser();
var store = s.CreateNewStore();
var (storeName, _) = s.CreateNewStore();
s.Driver.FindElement(By.Id("Apps")).Click();
s.Driver.FindElement(By.Id("CreateNewApp")).Click();
s.Driver.WaitForPageLoad();
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("SelectedAppType")).SendKeys("PointOfSale");
s.Driver.FindElement(By.Id("SelectedStore")).SendKeys(storeName);
s.Driver.FindElement(By.Id("Create")).Click();
s.Driver.FindElement(By.Id("DefaultView")).SendKeys("Cart" + Keys.Enter);
s.Driver.FindElement(By.Id("SaveSettings")).ForceClick();
s.Driver.FindElement(By.Id("ViewApp")).ForceClick();
s.Driver.FindElement(By.Id("DefaultView")).SendKeys("Cart");
s.Driver.FindElement(By.Id("SaveSettings")).Click();
s.Driver.FindElement(By.Id("ViewApp")).Click();
var posBaseUrl = s.Driver.Url.Replace("/Cart", "");
Assert.True(s.Driver.PageSource.Contains("Tea shop"), "Unable to create PoS");
Assert.True(s.Driver.PageSource.Contains("Cart"), "PoS not showing correct default view");
s.Driver.Url = posBaseUrl + "/static";
s.Driver.WaitForPageLoad();
Assert.False(s.Driver.PageSource.Contains("Cart"), "Static PoS not showing correct view");
s.Driver.Url = posBaseUrl + "/cart";
s.Driver.WaitForPageLoad();
Assert.True(s.Driver.PageSource.Contains("Cart"), "Cart PoS not showing correct view");
s.Driver.Quit();
}
}
@ -503,24 +475,24 @@ namespace BTCPayServer.Tests
{
await s.StartAsync();
s.RegisterNewUser();
var store = s.CreateNewStore();
var (storeName, _) = s.CreateNewStore();
s.AddDerivationScheme();
s.Driver.FindElement(By.Id("Apps")).Click();
s.Driver.FindElement(By.Id("CreateNewApp")).Click();
s.Driver.WaitForPageLoad();
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("SelectedAppType")).SendKeys("Crowdfund");
s.Driver.FindElement(By.Id("SelectedStore")).SendKeys(storeName);
s.Driver.FindElement(By.Id("Create")).Click();
s.Driver.WaitForPageLoad();
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();
s.Driver.FindElement(By.Id("SaveSettings")).Click();
s.Driver.FindElement(By.Id("ViewApp")).Click();
Assert.Equal("Currently Active!", s.Driver.FindElement(By.CssSelector(".h6.text-muted")).Text);
}
}
@ -539,11 +511,13 @@ namespace BTCPayServer.Tests
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.FindElement(By.Id("SaveButton")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.Name("ViewAppButton")).Click();
s.Driver.SwitchTo().Window(s.Driver.WindowHandles.Last());
Assert.True(s.Driver.PageSource.Contains("Amount due"), "Unable to create Payment Request");
s.Driver.Quit();
s.Driver.WaitForPageLoad();
Assert.Equal("Amount due", s.Driver.FindElement(By.CssSelector("[data-test='amount-due-title']")).Text);
}
}
@ -560,6 +534,7 @@ namespace BTCPayServer.Tests
var walletId = new WalletId(storeId, "BTC");
s.GoToWallet(walletId, WalletsNavPages.Receive);
s.Driver.FindElement(By.Id("generateButton")).Click();
s.Driver.WaitForPageLoad();
var addressStr = s.Driver.FindElement(By.Id("address")).GetProperty("value");
var address = BitcoinAddress.Create(addressStr, ((BTCPayNetwork)s.Server.NetworkProvider.GetNetwork("BTC")).NBitcoinNetwork);
await s.Server.ExplorerNode.GenerateAsync(1);
@ -584,11 +559,13 @@ namespace BTCPayServer.Tests
await s.Server.ExplorerNode.GenerateAsync(1);
s.GoToWallet(walletId, WalletsNavPages.Send);
s.Driver.FindElement(By.Id("advancedSettings")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.Id("toggleInputSelection")).Click();
s.Driver.WaitForElement(By.Id(spentOutpoint.ToString()));
s.Driver.FindElement(By.Id(spentOutpoint.ToString()));
Assert.Equal("true", s.Driver.FindElement(By.Name("InputSelection")).GetAttribute("value").ToLowerInvariant());
var el = s.Driver.FindElement(By.Id(spentOutpoint.ToString()));
s.Driver.FindElement(By.Id(spentOutpoint.ToString())).Click();
s.Driver.WaitForPageLoad();
var inputSelectionSelect = s.Driver.FindElement(By.Name("SelectedInputs"));
Assert.Single(inputSelectionSelect.FindElements(By.CssSelector("[selected]")));
@ -596,8 +573,8 @@ namespace BTCPayServer.Tests
SetTransactionOutput(s, 0, bob, 0.3m);
s.Driver.FindElement(By.Id("SendMenu")).Click();
s.Driver.FindElement(By.Id("spendWithNBxplorer")).Click();
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
var happyElement = s.AssertHappyMessage();
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
var happyElement = s.FindAlertMessage();
var happyText = happyElement.Text;
var txid = Regex.Match(happyText, @"\((.*)\)").Groups[1].Value;
@ -614,13 +591,14 @@ namespace BTCPayServer.Tests
{
await s.StartAsync();
s.RegisterNewUser(true);
var store = s.CreateNewStore();
s.GoToStore(store.storeId, Views.Stores.StoreNavPages.Webhooks);
var (storeName, storeId) = s.CreateNewStore();
s.GoToStore(storeId, Views.Stores.StoreNavPages.Webhooks);
Logs.Tester.LogInformation("Let's create two webhooks");
for (int i = 0; i < 2; i++)
for (var i = 0; i < 2; i++)
{
s.Driver.FindElement(By.Id("CreateWebhook")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.Name("PayloadUrl")).SendKeys($"http://127.0.0.1/callback{i}");
new SelectElement(s.Driver.FindElement(By.Name("Everything")))
.SelectByValue("false");
@ -636,7 +614,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("continue")).Click();
deletes = s.Driver.FindElements(By.LinkText("Delete"));
Assert.Single(deletes);
s.AssertHappyMessage();
s.FindAlertMessage();
Logs.Tester.LogInformation("Let's try to update one of them");
s.Driver.FindElement(By.LinkText("Modify")).Click();
@ -648,7 +626,7 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Name("Secret")).Clear();
s.Driver.FindElement(By.Name("Secret")).SendKeys("HelloWorld");
s.Driver.FindElement(By.Name("update")).Click();
s.AssertHappyMessage();
s.FindAlertMessage();
s.Driver.FindElement(By.LinkText("Modify")).Click();
foreach (var value in Enum.GetValues(typeof(WebhookEventType)))
{
@ -664,13 +642,13 @@ namespace BTCPayServer.Tests
Assert.DoesNotContain($"value=\"InvoiceReceivedPayment\" checked", s.Driver.PageSource);
s.Driver.FindElement(By.Name("update")).Click();
s.AssertHappyMessage();
s.FindAlertMessage();
Assert.Contains(server.ServerUri.AbsoluteUri, s.Driver.PageSource);
Logs.Tester.LogInformation("Let's see if we can generate an event");
s.GoToStore(store.storeId);
s.GoToStore(storeId);
s.AddDerivationScheme();
s.CreateInvoice(store.storeName);
s.CreateInvoice(storeName);
var request = await server.GetNextRequest();
var headers = request.Request.Headers;
var actualSig = headers["BTCPay-Sig"].First();
@ -681,21 +659,23 @@ namespace BTCPayServer.Tests
server.Done();
Logs.Tester.LogInformation("Let's make a failed event");
s.CreateInvoice(store.storeName);
s.CreateInvoice(storeName);
request = await server.GetNextRequest();
request.Response.StatusCode = 404;
server.Done();
// The delivery is done asynchronously, so small wait here
await Task.Delay(500);
s.GoToStore(store.storeId, Views.Stores.StoreNavPages.Webhooks);
s.GoToStore(storeId, Views.Stores.StoreNavPages.Webhooks);
s.Driver.FindElement(By.LinkText("Modify")).Click();
var elements = s.Driver.FindElements(By.ClassName("redeliver"));
// One worked, one failed
s.Driver.FindElement(By.ClassName("fa-times"));
s.Driver.FindElement(By.ClassName("fa-check"));
elements[0].Click();
s.AssertHappyMessage();
s.Driver.WaitForPageLoad();
s.FindAlertMessage();
request = await server.GetNextRequest();
request.Response.StatusCode = 404;
server.Done();
@ -708,32 +688,23 @@ namespace BTCPayServer.Tests
CanBrowseContent(s);
var element = s.Driver.FindElement(By.ClassName("redeliver"));
element.Click();
s.AssertHappyMessage();
s.Driver.WaitForPageLoad();
s.FindAlertMessage();
request = await server.GetNextRequest();
request.Response.StatusCode = 404;
server.Done();
Logs.Tester.LogInformation("Let's see if we can delete store with some webhooks inside");
s.GoToStore(store.storeId);
s.GoToStore(storeId);
s.Driver.ExecuteJavaScript("window.scrollBy(0,1000);");
s.Driver.FindElement(By.Id("danger-zone-expander")).Click();
s.Driver.FindElement(By.Id("delete-store")).Click();
s.Driver.FindElement(By.Id("continue")).Click();
s.AssertHappyMessage();
s.FindAlertMessage();
}
}
private static void CanBrowseContent(SeleniumTester s)
{
s.Driver.FindElement(By.ClassName("delivery-content")).Click();
var windows = s.Driver.WindowHandles;
Assert.Equal(2, windows.Count);
s.Driver.SwitchTo().Window(windows[1]);
JObject.Parse(s.Driver.FindElement(By.TagName("body")).Text);
s.Driver.Close();
s.Driver.SwitchTo().Window(windows[0]);
}
[Fact(Timeout = TestTimeout)]
public async Task CanManageWallet()
{
@ -749,23 +720,34 @@ namespace BTCPayServer.Tests
//let's test quickly the receive wallet page
s.Driver.FindElement(By.Id("Wallets")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.LinkText("Manage")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.Id("WalletSend")).Click();
s.Driver.ScrollTo(By.Id("SendMenu"));
s.Driver.FindElement(By.Id("SendMenu")).ForceClick();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.Id("SendMenu")).Click();
//you cant use the Sign with NBX option without saving private keys when generating the wallet.
Assert.DoesNotContain("nbx-seed", s.Driver.PageSource);
s.Driver.FindElement(By.Id("WalletReceive")).Click();
s.Driver.WaitForPageLoad();
//generate a receiving address
s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
Assert.True(s.Driver.FindElement(By.ClassName("qr-container")).Displayed);
var receiveAddr = s.Driver.FindElement(By.Id("address")).GetAttribute("value");
//unreserve
s.Driver.FindElement(By.CssSelector("button[value=unreserve-current-address]")).Click();
//generate it again, should be the same one as before as nothign got used in the meantime
s.Driver.WaitForPageLoad();
//generate it again, should be the same one as before as nothing got used in the meantime
s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
s.Driver.WaitForPageLoad();
Assert.True(s.Driver.FindElement(By.ClassName("qr-container")).Displayed);
Assert.Equal(receiveAddr, s.Driver.FindElement(By.Id("address")).GetAttribute("value"));
@ -779,6 +761,8 @@ namespace BTCPayServer.Tests
await Task.Delay(200);
s.Driver.Navigate().Refresh();
s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
s.Driver.WaitForPageLoad();
Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("address")).GetAttribute("value"));
receiveAddr = s.Driver.FindElement(By.Id("address")).GetAttribute("value");
@ -787,8 +771,12 @@ namespace BTCPayServer.Tests
s.GenerateWallet("BTC", "", true, false);
s.Driver.FindElement(By.Id("Wallets")).Click();
s.Driver.FindElement(By.LinkText("Manage")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.Id("WalletReceive")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click();
s.Driver.WaitForPageLoad();
Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("address")).GetAttribute("value"));
var invoiceId = s.CreateInvoice(storeId.storeName);
@ -818,16 +806,18 @@ namespace BTCPayServer.Tests
s.ClickOnAllSideMenus();
// Make sure we can rescan, because we are admin!
s.Driver.FindElement(By.Id("WalletRescan")).ForceClick();
s.Driver.FindElement(By.Id("WalletRescan")).Click();
Assert.Contains("The batch size make sure", s.Driver.PageSource);
// We setup the fingerprint and the account key path
s.Driver.FindElement(By.Id("WalletSettings")).ForceClick();
s.Driver.FindElement(By.Id("WalletSettings")).Click();
// 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();
s.Driver.FindElement(By.Id("WalletTransactions")).Click();
s.Driver.WaitForPageLoad();
var walletTransactionLink = s.Driver.Url;
Assert.Contains(tx.ToString(), s.Driver.PageSource);
@ -838,17 +828,18 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("WalletSend")).Click();
var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest);
SetTransactionOutput(s, 0, bob, 1);
s.Driver.ScrollTo(By.Id("SendMenu"));
s.Driver.FindElement(By.Id("SendMenu")).ForceClick();
s.Driver.FindElement(By.Id("SendMenu")).Click();
s.Driver.FindElement(By.CssSelector("button[value=seed]")).Click();
s.Driver.WaitForPageLoad();
// Input the seed
s.Driver.FindElement(By.Id("SeedOrKey")).SendKeys(signingSource.ToString() + Keys.Enter);
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();
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
s.Driver.WaitForPageLoad();
Assert.Equal(walletTransactionLink, s.Driver.Url);
}
@ -860,18 +851,17 @@ namespace BTCPayServer.Tests
var jack = new Key().PubKey.Hash.GetAddress(Network.RegTest);
SetTransactionOutput(s, 0, jack, 0.01m);
s.Driver.ScrollTo(By.Id("SendMenu"));
s.Driver.FindElement(By.Id("SendMenu")).ForceClick();
s.Driver.FindElement(By.Id("SendMenu")).Click();
s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
Assert.Contains(jack.ToString(), s.Driver.PageSource);
Assert.Contains("0.01000000", s.Driver.PageSource);
s.Driver.FindElement(By.CssSelector("button[value=analyze-psbt]")).ForceClick();
s.Driver.FindElement(By.CssSelector("button[value=analyze-psbt]")).Click();
Assert.EndsWith("psbt", s.Driver.Url);
s.Driver.FindElement(By.CssSelector("#OtherActions")).ForceClick();
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
s.Driver.FindElement(By.CssSelector("#OtherActions")).Click();
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
Assert.EndsWith("psbt/ready", s.Driver.Url);
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
Assert.Equal(walletTransactionLink, s.Driver.Url);
var bip21 = invoice.EntityToDTO().CryptoInfo.First().PaymentUrls.BIP21;
@ -884,14 +874,14 @@ namespace BTCPayServer.Tests
s.Driver.FindElement(By.Id("bip21parse")).Click();
s.Driver.SwitchTo().Alert().SendKeys(bip21);
s.Driver.SwitchTo().Alert().Accept();
s.AssertHappyMessage(StatusMessageModel.StatusSeverity.Info);
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Info);
Assert.Equal(parsedBip21.Amount.ToString(false), s.Driver.FindElement(By.Id($"Outputs_0__Amount")).GetAttribute("value"));
Assert.Equal(parsedBip21.Address.ToString(), s.Driver.FindElement(By.Id($"Outputs_0__DestinationAddress")).GetAttribute("value"));
s.GoToWallet(new WalletId(storeId.storeId, "BTC"), WalletsNavPages.Settings);
var walletUrl = s.Driver.Url;
s.Driver.FindElement(By.Id("SettingsMenu")).ForceClick();
s.Driver.FindElement(By.Id("SettingsMenu")).Click();
s.Driver.FindElement(By.CssSelector("button[value=view-seed]")).Click();
// Seed backup page
@ -905,18 +895,6 @@ namespace BTCPayServer.Tests
Assert.Equal(walletUrl, s.Driver.Url);
}
}
void SetTransactionOutput(SeleniumTester s, 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();
}
}
[Fact]
[Trait("Selenium", "Selenium")]
@ -932,39 +910,49 @@ namespace BTCPayServer.Tests
await s.FundStoreWallet(denomination: 50.0m);
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.Id("Name")).SendKeys("PP1");
s.Driver.FindElement(By.Id("Amount")).Clear();
s.Driver.FindElement(By.Id("Amount")).SendKeys("99.0" + Keys.Enter);
s.Driver.FindElement(By.Id("Amount")).SendKeys("99.0");;
s.Driver.FindElement(By.Id("Create")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.LinkText("View")).Click();
Thread.Sleep(1000);
s.GoToWallet(navPages: WalletsNavPages.PullPayments);
s.Driver.FindElement(By.Id("NewPullPayment")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.Id("Name")).SendKeys("PP2");
s.Driver.FindElement(By.Id("Amount")).Clear();
s.Driver.FindElement(By.Id("Amount")).SendKeys("100.0" + Keys.Enter);
s.Driver.FindElement(By.Id("Amount")).SendKeys("100.0");
s.Driver.FindElement(By.Id("Create")).Click();
s.Driver.WaitForPageLoad();
// This should select the first View, ie, the last one PP2
s.Driver.FindElement(By.LinkText("View")).Click();
s.Driver.WaitForPageLoad();
Thread.Sleep(1000);
var address = await s.Server.ExplorerNode.GetNewAddressAsync();
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("15" + Keys.Enter);
s.AssertHappyMessage();
s.FindAlertMessage();
// We should not be able to use an address already used
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("20" + Keys.Enter);
s.AssertHappyMessage(StatusMessageModel.StatusSeverity.Error);
s.FindAlertMessage(StatusMessageModel.StatusSeverity.Error);
address = await s.Server.ExplorerNode.GetNewAddressAsync();
s.Driver.FindElement(By.Id("Destination")).Clear();
s.Driver.FindElement(By.Id("Destination")).SendKeys(address.ToString());
s.Driver.FindElement(By.Id("ClaimedAmount")).Clear();
s.Driver.FindElement(By.Id("ClaimedAmount")).SendKeys("20" + Keys.Enter);
s.AssertHappyMessage();
s.FindAlertMessage();
Assert.Contains("Awaiting Approval", s.Driver.PageSource);
var viewPullPaymentUrl = s.Driver.Url;
@ -982,11 +970,18 @@ namespace BTCPayServer.Tests
Assert.DoesNotContain("No payout waiting for approval", s.Driver.PageSource);
s.Driver.FindElement(By.Id("selectAllCheckbox")).Click();
s.Driver.FindElement(By.Id("payCommand")).Click();
s.Driver.ScrollTo(By.Id("SendMenu"));
s.Driver.FindElement(By.Id("SendMenu")).ForceClick();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.Id("SendMenu")).Click();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click();
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick();
s.AssertHappyMessage();
s.Driver.WaitForPageLoad();
s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).Click();
s.Driver.WaitForPageLoad();
s.FindAlertMessage();
TestUtils.Eventually(() =>
{
@ -1027,5 +1022,59 @@ namespace BTCPayServer.Tests
});
}
}
private static void CanBrowseContent(SeleniumTester s)
{
s.Driver.FindElement(By.ClassName("delivery-content")).Click();
s.Driver.WaitForPageLoad();
var windows = s.Driver.WindowHandles;
Assert.Equal(2, windows.Count);
s.Driver.SwitchTo().Window(windows[1]);
JObject.Parse(s.Driver.FindElement(By.TagName("body")).Text);
s.Driver.Close();
s.Driver.SwitchTo().Window(windows[0]);
}
private static void CanSetupEmailCore(SeleniumTester s)
{
s.Driver.FindElement(By.ClassName("dropdown-toggle")).Click();
s.Driver.FindElement(By.ClassName("dropdown-item")).Click();
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test@gmail.com");
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
s.FindAlertMessage();
s.Driver.FindElement(By.Id("Settings_Password")).SendKeys("mypassword");
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
Assert.Contains("Configured", s.Driver.PageSource);
s.Driver.FindElement(By.Id("Settings_Login")).SendKeys("test_fix@gmail.com");
s.Driver.FindElement(By.CssSelector("button[value=\"Save\"]")).Submit();
s.Driver.WaitForPageLoad();
Assert.Contains("Configured", s.Driver.PageSource);
Assert.Contains("test_fix", s.Driver.PageSource);
s.Driver.FindElement(By.CssSelector("button[value=\"ResetPassword\"]")).Submit();
s.FindAlertMessage();
Assert.DoesNotContain("Configured", s.Driver.PageSource);
Assert.Contains("test_fix", s.Driver.PageSource);
}
private static string AssertUrlHasPairingCode(SeleniumTester s)
{
var regex = Regex.Match(new Uri(s.Driver.Url, UriKind.Absolute).Query, "pairingCode=([^&]*)");
Assert.True(regex.Success, $"{s.Driver.Url} does not match expected regex");
var pairingCode = regex.Groups[1].Value;
return pairingCode;
}
private void SetTransactionOutput(SeleniumTester s, 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(CultureInfo.InvariantCulture));
var checkboxElement = s.Driver.FindElement(By.Id($"Outputs_{index}__SubtractFeesFromOutput"));
if (checkboxElement.Selected != subtract)
{
checkboxElement.Click();
}
}
}
}

View File

@ -15,19 +15,19 @@
@Model.Title
@if (!Model.Started && Model.StartDate.HasValue)
{
<span class="h6 text-muted" >
<span class="h6 text-muted">
Starts @Model.StartDate.Value.Subtract(DateTime.Now.ToUniversalTime())
</span>
}
else if (Model.Started && !Model.Ended && Model.EndDate.HasValue)
{
<span class="h6 text-muted" >
<span class="h6 text-muted">
Ends @Model.EndDate.Value.Subtract(DateTime.Now.ToUniversalTime())
</span>
}
else if (Model.Started && !Model.Ended && !Model.EndDate.HasValue)
{
<span class="h6 text-muted" :title="startDate" title="No set end date">
<span class="h6 text-muted" :title="startDate" title="No set end date">
Currently Active!
</span>
}

View File

@ -216,7 +216,7 @@
<dl class="mb-0 mt-md-4">
<div class="d-flex d-print-inline-block flex-column mb-4 mr-5">
<dt class="h4 font-weight-normal text-nowrap text-primary text-print-default order-2 order-sm-1 mb-0" v-text="srvModel.amountDueFormatted">@Model.AmountDueFormatted</dt>
<dd class="text-muted order-1 order-sm-2 mb-1">Amount due</dd>
<dd class="text-muted order-1 order-sm-2 mb-1" data-test="amount-due-title">Amount due</dd>
</div>
<div class="progress bg-light d-none d-sm-flex mb-sm-4 d-print-none" style="height:5px">
<div class="progress-bar bg-primary" role="progressbar" style="width:@((Model.AmountCollected/Model.Amount)*100)%" v-bind:style="{ width: (srvModel.amountCollected/srvModel.amount*100) + '%' }"></div>