public async Task CanCreateStores() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); 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(); var invoiceId = s.CreateInvoice(store); s.AssertHappyMessage(); 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(); 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(); Assert.DoesNotContain("Archived", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text); s.GoToInvoices(); Assert.Contains(invoiceId, s.Driver.PageSource); // 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); s.GoToRegister(); // 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(); // 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(); // 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(); // Alice should be able to delete the store s.Logout(); LogIn(s, alice); s.Driver.FindElement(By.Id("Stores")).Click(); s.Driver.FindElement(By.LinkText("Remove")).Click(); s.Driver.FindElement(By.Id("continue")).Click(); s.Driver.FindElement(By.Id("Stores")).Click(); s.Driver.Navigate().GoToUrl(storeUrl); Assert.Contains("ReturnUrl", s.Driver.Url); } }
public void SetCheckbox(SeleniumTester s, string checkboxId, bool value) { SetCheckbox(s.Driver.FindElement(By.Id(checkboxId)), value); }
public async Task CanUseBIP79Client() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); var invoiceRepository = s.Server.PayTester.GetService <InvoiceRepository>(); // var payjoinRepository = s.Server.PayTester.GetService<PayJoinRepository>(); // var broadcaster = s.Server.PayTester.GetService<DelayedTransactionBroadcaster>(); s.RegisterNewUser(true); var receiver = s.CreateNewStore(); var receiverSeed = s.GenerateWallet("BTC", "", true, true); var receiverWalletId = new WalletId(receiver.storeId, "BTC"); //payjoin is not enabled by default. var invoiceId = s.CreateInvoice(receiver.storeId); s.GoToInvoiceCheckout(invoiceId); var bip21 = s.Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn")) .GetAttribute("href"); Assert.DoesNotContain("bpu=", bip21); s.GoToHome(); s.GoToStore(receiver.storeId); //payjoin is not enabled by default. Assert.False(s.Driver.FindElement(By.Id("PayJoinEnabled")).Selected); 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); var senderWalletId = new WalletId(sender.storeId, "BTC"); await s.Server.ExplorerNode.GenerateAsync(1); await s.FundStoreWallet(senderWalletId); invoiceId = s.CreateInvoice(receiver.storeId); s.GoToInvoiceCheckout(invoiceId); bip21 = s.Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn")) .GetAttribute("href"); Assert.Contains("bpu=", bip21); s.GoToWalletSend(senderWalletId); s.Driver.FindElement(By.Id("bip21parse")).Click(); s.Driver.SwitchTo().Alert().SendKeys(bip21); s.Driver.SwitchTo().Alert().Accept(); Assert.False(string.IsNullOrEmpty(s.Driver.FindElement(By.Id("PayJoinEndpointUrl")).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(); await s.Server.WaitForEvent <NewOnChainTransactionEvent>(() => { s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).ForceClick(); return(Task.CompletedTask); }); //no funds in receiver wallet to do payjoin s.AssertHappyMessage(StatusMessageModel.StatusSeverity.Warning); await TestUtils.EventuallyAsync(async() => { var invoice = await s.Server.PayTester.GetService <InvoiceRepository>().GetInvoice(invoiceId); Assert.Equal(InvoiceStatus.Paid, invoice.Status); }); s.GoToInvoices(); var paymentValueRowColumn = s.Driver.FindElement(By.Id($"invoice_{invoiceId}")).FindElement(By.ClassName("payment-value")); Assert.False(paymentValueRowColumn.Text.Contains("payjoin", StringComparison.InvariantCultureIgnoreCase)); //let's do it all again, except now the receiver has funds and is able to payjoin invoiceId = s.CreateInvoice(receiver.storeId); s.GoToInvoiceCheckout(invoiceId); bip21 = s.Driver.FindElement(By.ClassName("payment__details__instruction__open-wallet__btn")) .GetAttribute("href"); Assert.Contains("bpu", bip21); s.GoToWalletSend(senderWalletId); s.Driver.FindElement(By.Id("bip21parse")).Click(); s.Driver.SwitchTo().Alert().SendKeys(bip21); s.Driver.SwitchTo().Alert().Accept(); Assert.False(string.IsNullOrEmpty(s.Driver.FindElement(By.Id("PayJoinEndpointUrl")).GetAttribute("value"))); s.Driver.FindElement(By.Id("FeeSatoshiPerByte")).Clear(); s.Driver.FindElement(By.Id("FeeSatoshiPerByte")).SendKeys("1"); s.Driver.ScrollTo(By.Id("SendMenu")); s.Driver.FindElement(By.Id("SendMenu")).ForceClick(); s.Driver.FindElement(By.CssSelector("button[value=nbx-seed]")).Click(); await s.Server.WaitForEvent <NewOnChainTransactionEvent>(() => { s.Driver.FindElement(By.CssSelector("button[value=payjoin]")).ForceClick(); return(Task.CompletedTask); }); s.AssertHappyMessage(StatusMessageModel.StatusSeverity.Success); await TestUtils.EventuallyAsync(async() => { var invoice = await invoiceRepository.GetInvoice(invoiceId); var payments = invoice.GetPayments(); Assert.Equal(2, payments.Count); var originalPayment = payments[0]; var coinjoinPayment = payments[1]; Assert.Equal(-1, ((BitcoinLikePaymentData)originalPayment.GetCryptoPaymentData()).ConfirmationCount); Assert.Equal(0, ((BitcoinLikePaymentData)coinjoinPayment.GetCryptoPaymentData()).ConfirmationCount); Assert.False(originalPayment.Accounted); Assert.True(coinjoinPayment.Accounted); Assert.Equal(((BitcoinLikePaymentData)originalPayment.GetCryptoPaymentData()).Value, ((BitcoinLikePaymentData)coinjoinPayment.GetCryptoPaymentData()).Value); Assert.Equal(originalPayment.GetCryptoPaymentData() .AssertType <BitcoinLikePaymentData>() .Value, coinjoinPayment.GetCryptoPaymentData() .AssertType <BitcoinLikePaymentData>() .Value); }); await TestUtils.EventuallyAsync(async() => { var invoice = await s.Server.PayTester.GetService <InvoiceRepository>().GetInvoice(invoiceId); var dto = invoice.EntityToDTO(); Assert.Equal(InvoiceStatus.Paid, invoice.Status); }); s.GoToInvoices(); paymentValueRowColumn = s.Driver.FindElement(By.Id($"invoice_{invoiceId}")).FindElement(By.ClassName("payment-value")); Assert.False(paymentValueRowColumn.Text.Contains("payjoin", StringComparison.InvariantCultureIgnoreCase)); } }
public async Task NewUserLogin() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); //Register & Log Out var email = s.RegisterNewUser(); s.Logout(); s.Driver.AssertNoError(); Assert.Contains("Account/Login", s.Driver.Url); // Should show the Tor address Assert.Contains("wsaxew3qa5ljfuenfebmaf3m5ykgatct3p6zjrqwoouj3foererde3id.onion", s.Driver.PageSource); s.Driver.Navigate().GoToUrl(s.Link("/invoices")); Assert.Contains("ReturnUrl=%2Finvoices", s.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 invoice Assert.EndsWith("/invoices", s.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); //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(); //Log In With New Password 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"); s.Driver.FindElement(By.Id("MySettings")).Click(); s.ClickOnAllSideMenus(); //let's test invite link s.Logout(); s.GoToRegister(); var newAdminUser = s.RegisterNewUser(true); s.GoToServer(ServerNavPages.Users); s.Driver.FindElement(By.Id("CreateUser")).Click(); 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;; s.Logout(); s.Driver.Navigate().GoToUrl(url); Assert.Equal("hidden", s.Driver.FindElement(By.Id("Email")).GetAttribute("type")); Assert.Equal(usr, s.Driver.FindElement(By.Id("Email")).GetAttribute("value")); 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.Driver.FindElement(By.Id("Email")).SendKeys(usr); s.Driver.FindElement(By.Id("Password")).SendKeys("123456"); s.Driver.FindElement(By.Id("LoginButton")).Click(); // We should be logged in now s.Driver.FindElement(By.Id("mainNav")); } }
public async Task CanManageWallet() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); s.RegisterNewUser(true); s.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(); s.AddDerivationScheme("BTC", "ypub6WWc2gWwHbdnAAyJDnR4SPL1phRh7REqrPBfZeizaQ1EmTshieRXJC3Z5YoU4wkcdKHEjQGkh6AYEzCQC1Kz3DNaWSwdc1pc8416hAjzqyD"); var tx = s.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create("bcrt1qmxg8fgnmkp354vhe78j6sr4ut64tyz2xyejel4", Network.RegTest), Money.Coins(3.0m)); s.Server.ExplorerNode.Generate(1); s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.ClickOnAllSideMenus(); // Make sure we can rescan, because we are admin! s.Driver.FindElement(By.Id("WalletRescan")).ForceClick(); 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("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); } }
public async Task CanUsePullPaymentsViaUI() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); s.RegisterNewUser(true); var receiver = s.CreateNewStore(); var receiverSeed = s.GenerateWallet("BTC", "", true, true, ScriptPubKeyType.Segwit); await s.Server.ExplorerNode.GenerateAsync(1); await s.FundStoreWallet(denomination : 50.0m); s.GoToWallet(navPages: WalletsNavPages.PullPayments); s.Driver.FindElement(By.Id("NewPullPayment")).Click(); 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.LinkText("View")).Click(); Thread.Sleep(1000); s.GoToWallet(navPages: WalletsNavPages.PullPayments); s.Driver.FindElement(By.Id("NewPullPayment")).Click(); 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); // This should select the first View, ie, the last one PP2 s.Driver.FindElement(By.LinkText("View")).Click(); 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(); // 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); 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(); Assert.Contains("AwaitingPayment", s.Driver.PageSource); var viewPullPaymentUrl = s.Driver.Url; // This one should have nothing s.GoToWallet(navPages: WalletsNavPages.PullPayments); var payouts = s.Driver.FindElements(By.ClassName("pp-payout")); Assert.Equal(2, payouts.Count); payouts[1].Click(); Assert.Contains("No payout waiting for approval", s.Driver.PageSource); // PP2 should have payouts s.GoToWallet(navPages: WalletsNavPages.PullPayments); payouts = s.Driver.FindElements(By.ClassName("pp-payout")); payouts[0].Click(); 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.FindElement(By.CssSelector("button[value=nbx-seed]")).Click(); s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick(); s.AssertHappyMessage(); TestUtils.Eventually(() => { s.Driver.Navigate().Refresh(); Assert.Contains("badge transactionLabel", s.Driver.PageSource); }); Assert.Equal("Payout", s.Driver.FindElement(By.ClassName("transactionLabel")).Text); s.GoToWallet(navPages: WalletsNavPages.Payouts); TestUtils.Eventually(() => { s.Driver.Navigate().Refresh(); Assert.Contains("No payout waiting for approval", s.Driver.PageSource); }); var txs = s.Driver.FindElements(By.ClassName("transaction-link")); Assert.Equal(2, txs.Count); s.Driver.Navigate().GoToUrl(viewPullPaymentUrl); txs = s.Driver.FindElements(By.ClassName("transaction-link")); Assert.Equal(2, txs.Count); Assert.Contains("InProgress", s.Driver.PageSource); await s.Server.ExplorerNode.GenerateAsync(1); TestUtils.Eventually(() => { s.Driver.Navigate().Refresh(); Assert.Contains("Completed", s.Driver.PageSource); }); await s.Server.ExplorerNode.GenerateAsync(10); var pullPaymentId = viewPullPaymentUrl.Split('/').Last(); await TestUtils.EventuallyAsync(async() => { using var ctx = s.Server.PayTester.GetService <ApplicationDbContextFactory>().CreateContext(); var payoutsData = await ctx.Payouts.Where(p => p.PullPaymentDataId == pullPaymentId).ToListAsync(); Assert.True(payoutsData.All(p => p.State == Data.PayoutState.Completed)); }); } }
public async Task CanCreateApiKeys() { //there are 2 ways to create api keys: //as a user through your profile //as an external application requesting an api key from a user using (var s = SeleniumTester.Create()) { await s.StartAsync(); var tester = s.Server; var user = tester.NewAccount(); user.GrantAccess(); await user.MakeAdmin(false); s.GoToLogin(); s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); s.GoToProfile(ManageNavPages.APIKeys); s.Driver.FindElement(By.Id("AddApiKey")).Click(); //not an admin, so this permission should not show Assert.DoesNotContain("btcpay.server.canmodifyserversettings", s.Driver.PageSource); await user.MakeAdmin(); s.Logout(); s.GoToLogin(); s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); s.GoToProfile(ManageNavPages.APIKeys); s.Driver.FindElement(By.Id("AddApiKey")).Click(); Assert.Contains("btcpay.server.canmodifyserversettings", s.Driver.PageSource); //server management should show now s.SetCheckbox(s, "btcpay.server.canmodifyserversettings", true); s.SetCheckbox(s, "btcpay.store.canmodifystoresettings", true); s.SetCheckbox(s, "btcpay.user.canviewprofile", true); s.Driver.FindElement(By.Id("Generate")).Click(); 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); s.Driver.FindElement(By.Id("AddApiKey")).Click(); s.SetCheckbox(s, "btcpay.server.canmodifyserversettings", true); s.Driver.FindElement(By.Id("Generate")).Click(); var serverOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text; await TestApiAgainstAccessToken(serverOnlyApiKey, tester, user, Policies.CanModifyServerSettings); s.Driver.FindElement(By.Id("AddApiKey")).Click(); s.SetCheckbox(s, "btcpay.store.canmodifystoresettings", true); s.Driver.FindElement(By.Id("Generate")).Click(); var allStoreOnlyApiKey = s.FindAlertMessage().FindElement(By.TagName("code")).Text; await TestApiAgainstAccessToken(allStoreOnlyApiKey, tester, user, Policies.CanModifyStoreSettings); s.Driver.FindElement(By.Id("AddApiKey")).Click(); s.Driver.FindElement(By.CssSelector("button[value='btcpay.store.canmodifystoresettings:change-store-mode']")).Click(); //there should be a store already by default in the dropdown var dropdown = s.Driver.FindElement(By.Name("PermissionValues[4].SpecificStores[0]")); var option = dropdown.FindElement(By.TagName("option")); var storeId = option.GetAttribute("value"); option.Click(); s.Driver.FindElement(By.Id("Generate")).Click(); 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.FindAlertMessage().FindElement(By.TagName("code")).Text; await TestApiAgainstAccessToken(noPermissionsApiKey, tester, user); await Assert.ThrowsAnyAsync <HttpRequestException>(async() => { await TestApiAgainstAccessToken <bool>("incorrect key", $"{TestApiPath}/me/id", tester.PayTester.HttpClient); }); //let's test the authorized screen now //options for authorize are: //applicationName //redirect //permissions //strict //selectiveStores //redirect //appidentifier var appidentifier = "testapp"; var callbackUrl = tester.PayTester.ServerUri + "postredirect-callback-test"; var authUrl = BTCPayServerClient.GenerateAuthorizeUri(tester.PayTester.ServerUri, new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, applicationDetails: (appidentifier, new Uri(callbackUrl))).ToString(); s.Driver.Navigate().GoToUrl(authUrl); Assert.Contains(appidentifier, s.Driver.PageSource); Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant()); Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant()); Assert.DoesNotContain("change-store-mode", s.Driver.PageSource); s.Driver.FindElement(By.Id("consent-yes")).Click(); Assert.Equal(callbackUrl, s.Driver.Url); var apiKeyRepo = s.Server.PayTester.GetService <APIKeyRepository>(); var accessToken = GetAccessTokenFromCallbackResult(s.Driver); await TestApiAgainstAccessToken(accessToken, tester, user, (await apiKeyRepo.GetKey(accessToken)).GetBlob().Permissions); authUrl = BTCPayServerClient.GenerateAuthorizeUri(tester.PayTester.ServerUri, new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, applicationDetails: (null, new Uri(callbackUrl))).ToString(); s.Driver.Navigate().GoToUrl(authUrl); Assert.DoesNotContain("kukksappname", s.Driver.PageSource); Assert.Equal("checkbox", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant()); Assert.Equal("checkbox", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant()); s.SetCheckbox(s, "btcpay.server.canmodifyserversettings", false); Assert.Contains("change-store-mode", s.Driver.PageSource); s.Driver.FindElement(By.Id("consent-yes")).Click(); Assert.Equal(callbackUrl, s.Driver.Url); accessToken = GetAccessTokenFromCallbackResult(s.Driver); await TestApiAgainstAccessToken(accessToken, tester, user, (await apiKeyRepo.GetKey(accessToken)).GetBlob().Permissions); //let's test the app identifier system authUrl = BTCPayServerClient.GenerateAuthorizeUri(tester.PayTester.ServerUri, new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, (appidentifier, new Uri(callbackUrl))).ToString(); //if it's the same, go to the confirm page s.Driver.Navigate().GoToUrl(authUrl); s.Driver.FindElement(By.Id("continue")).Click(); Assert.Equal(callbackUrl, s.Driver.Url); //same app but different redirect = nono authUrl = BTCPayServerClient.GenerateAuthorizeUri(tester.PayTester.ServerUri, new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true, (appidentifier, new Uri("https://international.local/callback"))).ToString(); s.Driver.Navigate().GoToUrl(authUrl); Assert.False(s.Driver.Url.StartsWith("https://international.com/callback")); // Make sure we can check all permissions when not an admin await user.MakeAdmin(false); s.Logout(); s.GoToLogin(); s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); s.GoToProfile(ManageNavPages.APIKeys); s.Driver.FindElement(By.Id("AddApiKey")).Click(); int checkedPermissionCount = 0; foreach (var checkbox in s.Driver.FindElements(By.ClassName("form-check-input"))) { checkedPermissionCount++; checkbox.Click(); } s.Driver.FindElement(By.Id("Generate")).Click(); 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); } }
public async Task CanUseCodeFlow() { using (var s = SeleniumTester.Create()) { s.Start(); var tester = s.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() { 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]); var httpClient = tester.PayTester.HttpClient; var httpRequest = new HttpRequestMessage(HttpMethod.Post, new Uri(tester.PayTester.ServerUri, "/connect/token")) { Content = new FormUrlEncodedContent(new List <KeyValuePair <string, string> >() { new KeyValuePair <string, string>("grant_type", OpenIddictConstants.GrantTypes.AuthorizationCode), new KeyValuePair <string, string>("client_id", openIdClient.ClientId), new KeyValuePair <string, string>("client_secret", secret), new KeyValuePair <string, string>("code", results["code"]), new KeyValuePair <string, string>("redirect_uri", redirecturi.AbsoluteUri) }) }; var response = await httpClient.SendAsync(httpRequest); Assert.True(response.IsSuccessStatusCode); string content = await response.Content.ReadAsStringAsync(); var result = JObject.Parse(content).ToObject <OpenIdConnectResponse>(); await TestApiAgainstAccessToken(result.AccessToken, tester, user); 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 <NoSuchElementException>(() => 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")); } }
public async Task CanManageWallet() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); s.RegisterNewUser(true); var storeId = s.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 = s.GenerateWallet("BTC", "", true, false); var invoiceId = s.CreateInvoice(storeId.storeId); var invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId); var address = invoice.EntityToDTO().Addresses["BTC"]; var result = await s.Server.ExplorerNode.GetAddressInfoAsync(BitcoinAddress.Create(address, Network.RegTest)); Assert.True(result.IsWatchOnly); s.GoToStore(storeId.storeId); mnemonic = s.GenerateWallet("BTC", "", true, true); var root = new Mnemonic(mnemonic).DeriveExtKey(); invoiceId = s.CreateInvoice(storeId.storeId); invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId); address = invoice.EntityToDTO().Addresses["BTC"]; result = await s.Server.ExplorerNode.GetAddressInfoAsync(BitcoinAddress.Create(address, Network.RegTest)); Assert.False(result.IsWatchOnly); var tx = s.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create(address, Network.RegTest), Money.Coins(3.0m)); s.Server.ExplorerNode.Generate(1); s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.ClickOnAllSideMenus(); // Make sure we can rescan, because we are admin! s.Driver.FindElement(By.Id("WalletRescan")).ForceClick(); 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("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); } }
private static async Task CanCreateRefundsCore(SeleniumTester s, TestAccount user, bool multiCurrency, string rateSelection) { s.GoToHome(); s.Server.PayTester.ChangeRate("BTC_USD", new Rating.BidAsk(5000.0m, 5100.0m)); var invoice = await user.BitPay.CreateInvoiceAsync(new Invoice { Currency = "USD", Price = 5000.0m }); var info = invoice.CryptoInfo.First(o => o.CryptoCode == "BTC"); var totalDue = decimal.Parse(info.TotalDue, CultureInfo.InvariantCulture); var paid = totalDue + 0.1m; await s.Server.ExplorerNode.SendToAddressAsync(BitcoinAddress.Create(info.Address, Network.RegTest), Money.Coins(paid)); await s.Server.ExplorerNode.GenerateAsync(1); await TestUtils.EventuallyAsync(async() => { invoice = await user.BitPay.GetInvoiceAsync(invoice.Id); Assert.Equal("confirmed", invoice.Status); }); // BTC crash by 50% s.Server.PayTester.ChangeRate("BTC_USD", new Rating.BidAsk(5000.0m / 2.0m, 5100.0m / 2.0m)); s.GoToStore(); s.Driver.FindElement(By.Id("BOLT11Expiration")).Clear(); s.Driver.FindElement(By.Id("BOLT11Expiration")).SendKeys("5" + Keys.Enter); s.GoToInvoice(invoice.Id); s.Driver.FindElement(By.Id("IssueRefund")).Click(); if (multiCurrency) { s.Driver.WaitUntilAvailable(By.Id("RefundForm"), TimeSpan.FromSeconds(1)); s.Driver.WaitUntilAvailable(By.Id("SelectedPaymentMethod"), TimeSpan.FromSeconds(1)); s.Driver.FindElement(By.Id("SelectedPaymentMethod")).SendKeys("BTC" + Keys.Enter); s.Driver.FindElement(By.Id("ok")).Click(); } s.Driver.WaitUntilAvailable(By.Id("RefundForm"), TimeSpan.FromSeconds(1)); Assert.Contains("$5,500.00", s.Driver.PageSource); // Should propose reimburse in fiat Assert.Contains("1.10000000 ₿", s.Driver.PageSource); // Should propose reimburse in BTC at the rate of before Assert.Contains("2.20000000 ₿", s.Driver.PageSource); // Should propose reimburse in BTC at the current rate s.Driver.WaitForAndClick(By.Id(rateSelection)); s.Driver.FindElement(By.Id("ok")).Click(); s.Driver.WaitUntilAvailable(By.Id("Destination"), TimeSpan.FromSeconds(1)); Assert.Contains("pull-payments", s.Driver.Url); if (rateSelection == "FiatOption") { Assert.Contains("$5,500.00", s.Driver.PageSource); } if (rateSelection == "CurrentOption") { Assert.Contains("2.20000000 ₿", s.Driver.PageSource); } if (rateSelection == "RateThenOption") { Assert.Contains("1.10000000 ₿", s.Driver.PageSource); } s.GoToInvoice(invoice.Id); s.Driver.FindElement(By.Id("IssueRefund")).Click(); s.Driver.WaitUntilAvailable(By.Id("Destination"), TimeSpan.FromSeconds(1)); Assert.Contains("pull-payments", s.Driver.Url); var client = await user.CreateClient(); var ppid = s.Driver.Url.Split('/').Last(); var pps = await client.GetPullPayments(user.StoreId); var pp = Assert.Single(pps, p => p.Id == ppid); Assert.Equal(TimeSpan.FromDays(5.0), pp.BOLT11Expiration); }
private static void SendAllTo(SeleniumTester s, string address) { s.Driver.FindElement(By.Name("Outputs[0].DestinationAddress")).SendKeys(address); s.Driver.FindElement(By.ClassName("crypto-balance-link")).Click(); s.Driver.FindElement(By.Id("SignTransaction")).Click(); }
public async Task CanCreateStores() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); var alice = s.RegisterNewUser(); var storeData = s.CreateNewStore(); // verify that hints are displayed on the store page Assert.True(s.Driver.PageSource.Contains("Wallet not setup for the store, please provide Derviation Scheme"), "Wallet hint not present"); Assert.True(s.Driver.PageSource.Contains("Review settings if you want to receive Lightning payments"), "Lightning hint not present"); s.GoToStores(); Assert.True(s.Driver.PageSource.Contains("warninghint_" + storeData.storeId), "Warning hint on list not present"); s.GoToStore(storeData.storeId); s.AddDerivationScheme(); // wallet hint should be dismissed s.Driver.AssertNoError(); Assert.False(s.Driver.PageSource.Contains("Wallet not setup for the store, please provide Derviation Scheme"), "Wallet hint not dismissed on derivation scheme add"); s.Driver.FindElement(By.Id("dismissLightningHint")).Click(); // dismiss lightning hint Assert.Contains(storeData.storeName, s.Driver.PageSource); var storeUrl = s.Driver.Url; s.ClickOnAllSideMenus(); s.GoToInvoices(); var invoiceId = s.CreateInvoice(storeData.storeName); s.AssertHappyMessage(); 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(); 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(); Assert.DoesNotContain("Archived", s.Driver.FindElement(By.Id("btn-archive-toggle")).Text); s.GoToInvoices(); Assert.Contains(invoiceId, s.Driver.PageSource); // 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); s.GoToRegister(); // 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(); // 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(); // 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(); // Alice should be able to delete the store s.Logout(); LogIn(s, alice); s.Driver.FindElement(By.Id("Stores")).Click(); // there shouldn't be any hints now Assert.False(s.Driver.PageSource.Contains("Review settings if you want to receive Lightning payments"), "Lightning hint should be dismissed at this point"); s.Driver.FindElement(By.LinkText("Remove")).Click(); s.Driver.FindElement(By.Id("continue")).Click(); s.Driver.FindElement(By.Id("Stores")).Click(); s.Driver.Navigate().GoToUrl(storeUrl); Assert.Contains("ReturnUrl", s.Driver.Url); } }
public async Task CanUseImplicitFlow() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); var tester = s.Server; var user = tester.NewAccount(); user.GrantAccess(); await user.MakeAdmin(); 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 server_management store_management&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 stores = await TestApiAgainstAccessToken <StoreData[]>(results["access_token"], $"api/test/me/stores", tester.PayTester.HttpClient); Assert.NotEmpty(stores); Assert.True(await TestApiAgainstAccessToken <bool>(results["access_token"], $"api/test/me/stores/{stores[0].Id}/can-edit", tester.PayTester.HttpClient)); //we dont ask for consent after acquiring it the first time for the same scopes. LogoutFlow(tester, id, s); s.Driver.Navigate().GoToUrl(implicitAuthorizeUrl); s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); s.Driver.AssertElementNotFound(By.Id("consent-yes")); // Let's asks without scopes LogoutFlow(tester, id, s); id = Guid.NewGuid().ToString(); openIdClient = await user.RegisterOpenIdClient( new OpenIddictApplicationDescriptor() { ClientId = id, DisplayName = id, Permissions = { OpenIddictConstants.Permissions.GrantTypes.Implicit }, RedirectUris = { redirecturi }, }); 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(); results = s.Driver.Url.Split("#").Last().Split("&") .ToDictionary(s1 => s1.Split("=")[0], s1 => s1.Split("=")[1]); await Assert.ThrowsAnyAsync <HttpRequestException>(async() => { await TestApiAgainstAccessToken <StoreData[]>(results["access_token"], $"api/test/me/stores", tester.PayTester.HttpClient); }); await Assert.ThrowsAnyAsync <HttpRequestException>(async() => { await TestApiAgainstAccessToken <bool>(results["access_token"], $"api/test/me/stores/{stores[0].Id}/can-edit", tester.PayTester.HttpClient); }); } }
public async Task CanCreateApiKeys() { //there are 2 ways to create api keys: //as a user through your profile //as an external application requesting an api key from a user using (var s = SeleniumTester.Create()) { await s.StartAsync(); var tester = s.Server; var user = tester.NewAccount(); user.GrantAccess(); await user.CreateStoreAsync(); s.GoToLogin(); s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); s.GoToProfile(ManageNavPages.APIKeys); s.Driver.FindElement(By.Id("AddApiKey")).Click(); if (!user.IsAdmin) { //not an admin, so this permission should not show Assert.DoesNotContain("ServerManagementPermission", s.Driver.PageSource); await user.MakeAdmin(); s.Logout(); s.GoToLogin(); s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); s.GoToProfile(ManageNavPages.APIKeys); s.Driver.FindElement(By.Id("AddApiKey")).Click(); } //server management should show now s.SetCheckbox(s, "ServerManagementPermission", true); s.SetCheckbox(s, "StoreManagementPermission", true); s.Driver.FindElement(By.Id("Generate")).Click(); var superApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text; //this api key has access to everything await TestApiAgainstAccessToken(superApiKey, tester, user, APIKeyConstants.Permissions.ServerManagement, APIKeyConstants.Permissions.StoreManagement); s.Driver.FindElement(By.Id("AddApiKey")).Click(); s.SetCheckbox(s, "ServerManagementPermission", true); s.Driver.FindElement(By.Id("Generate")).Click(); var serverOnlyApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text; await TestApiAgainstAccessToken(serverOnlyApiKey, tester, user, APIKeyConstants.Permissions.ServerManagement); s.Driver.FindElement(By.Id("AddApiKey")).Click(); s.SetCheckbox(s, "StoreManagementPermission", true); s.Driver.FindElement(By.Id("Generate")).Click(); var allStoreOnlyApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text; await TestApiAgainstAccessToken(allStoreOnlyApiKey, tester, user, APIKeyConstants.Permissions.StoreManagement); s.Driver.FindElement(By.Id("AddApiKey")).Click(); s.Driver.FindElement(By.CssSelector("button[value=change-store-mode]")).Click(); //there should be a store already by default in the dropdown var dropdown = s.Driver.FindElement(By.Name("SpecificStores[0]")); var option = dropdown.FindElement(By.TagName("option")); var storeId = option.GetAttribute("value"); option.Click(); s.Driver.FindElement(By.Id("Generate")).Click(); var selectiveStoreApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text; await TestApiAgainstAccessToken(selectiveStoreApiKey, tester, user, APIKeyConstants.Permissions.GetStorePermission(storeId)); s.Driver.FindElement(By.Id("AddApiKey")).Click(); s.Driver.FindElement(By.Id("Generate")).Click(); var noPermissionsApiKey = s.AssertHappyMessage().FindElement(By.TagName("code")).Text; await TestApiAgainstAccessToken(noPermissionsApiKey, tester, user); await Assert.ThrowsAnyAsync <HttpRequestException>(async() => { await TestApiAgainstAccessToken <bool>("incorrect key", $"{TestApiPath}/me/id", tester.PayTester.HttpClient); }); //let's test the authorized screen now //options for authorize are: //applicationName //redirect //permissions //strict //selectiveStores UriBuilder authorize = new UriBuilder(tester.PayTester.ServerUri); authorize.Path = "api-keys/authorize"; authorize.AppendPayloadToQuery(new Dictionary <string, object>() { { "redirect", "https://local.local/callback" }, { "applicationName", "kukksappname" }, { "strict", true }, { "selectiveStores", false }, { "permissions", new[] { APIKeyConstants.Permissions.StoreManagement, APIKeyConstants.Permissions.ServerManagement } }, }); var authUrl = authorize.ToString(); var perms = new[] { APIKeyConstants.Permissions.StoreManagement, APIKeyConstants.Permissions.ServerManagement }; authUrl = authUrl.Replace("permissions=System.String%5B%5D", string.Join("&", perms.Select(s1 => $"permissions={s1}"))); s.Driver.Navigate().GoToUrl(authUrl); s.Driver.PageSource.Contains("kukksappname"); Assert.NotNull(s.Driver.FindElement(By.Id("StoreManagementPermission")).GetAttribute("readonly")); Assert.True(s.Driver.FindElement(By.Id("StoreManagementPermission")).Selected); Assert.NotNull(s.Driver.FindElement(By.Id("ServerManagementPermission")).GetAttribute("readonly")); Assert.True(s.Driver.FindElement(By.Id("ServerManagementPermission")).Selected); Assert.DoesNotContain("change-store-mode", s.Driver.PageSource); s.Driver.FindElement(By.Id("consent-yes")).Click(); var url = s.Driver.Url; IEnumerable <KeyValuePair <string, string> > results = url.Split("?").Last().Split("&") .Select(s1 => new KeyValuePair <string, string>(s1.Split("=")[0], s1.Split("=")[1])); var apiKeyRepo = s.Server.PayTester.GetService <APIKeyRepository>(); await TestApiAgainstAccessToken(results.Single(pair => pair.Key == "key").Value, tester, user, (await apiKeyRepo.GetKey(results.Single(pair => pair.Key == "key").Value)).GetPermissions()); authorize = new UriBuilder(tester.PayTester.ServerUri); authorize.Path = "api-keys/authorize"; authorize.AppendPayloadToQuery(new Dictionary <string, object>() { { "strict", false }, { "selectiveStores", true }, { "permissions", new[] { APIKeyConstants.Permissions.StoreManagement, APIKeyConstants.Permissions.ServerManagement } } }); authUrl = authorize.ToString(); perms = new[] { APIKeyConstants.Permissions.StoreManagement, APIKeyConstants.Permissions.ServerManagement }; authUrl = authUrl.Replace("permissions=System.String%5B%5D", string.Join("&", perms.Select(s1 => $"permissions={s1}"))); s.Driver.Navigate().GoToUrl(authUrl); Assert.DoesNotContain("kukksappname", s.Driver.PageSource); Assert.Null(s.Driver.FindElement(By.Id("StoreManagementPermission")).GetAttribute("readonly")); Assert.True(s.Driver.FindElement(By.Id("StoreManagementPermission")).Selected); Assert.Null(s.Driver.FindElement(By.Id("ServerManagementPermission")).GetAttribute("readonly")); Assert.True(s.Driver.FindElement(By.Id("ServerManagementPermission")).Selected); s.SetCheckbox(s, "ServerManagementPermission", false); Assert.Contains("change-store-mode", s.Driver.PageSource); s.Driver.FindElement(By.Id("consent-yes")).Click(); url = s.Driver.Url; results = url.Split("?").Last().Split("&") .Select(s1 => new KeyValuePair <string, string>(s1.Split("=")[0], s1.Split("=")[1])); await TestApiAgainstAccessToken(results.Single(pair => pair.Key == "key").Value, tester, user, (await apiKeyRepo.GetKey(results.Single(pair => pair.Key == "key").Value)).GetPermissions()); } }
public async Task CanUseCoinSelection() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); var userId = s.RegisterNewUser(true); var storeId = s.CreateNewStore().storeId; s.GenerateWallet("BTC", "", false, true); var walletId = new WalletId(storeId, "BTC"); s.GoToWallet(walletId, WalletsNavPages.Receive); s.Driver.FindElement(By.Id("generateButton")).Click(); var addressStr = s.Driver.FindElement(By.Id("vue-address")).GetProperty("value"); var address = BitcoinAddress.Create(addressStr, ((BTCPayNetwork)s.Server.NetworkProvider.GetNetwork("BTC")).NBitcoinNetwork); await s.Server.ExplorerNode.GenerateAsync(1); for (int i = 0; i < 6; i++) { await s.Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(1.0m)); } var targetTx = await s.Server.ExplorerNode.SendToAddressAsync(address, Money.Coins(1.2m)); var tx = await s.Server.ExplorerNode.GetRawTransactionAsync(targetTx); var spentOutpoint = new OutPoint(targetTx, tx.Outputs.FindIndex(txout => txout.Value == Money.Coins(1.2m))); await TestUtils.EventuallyAsync(async() => { var store = await s.Server.PayTester.StoreRepository.FindStore(storeId); var x = store.GetSupportedPaymentMethods(s.Server.NetworkProvider) .OfType <DerivationSchemeSettings>() .Single(settings => settings.PaymentId.CryptoCode == walletId.CryptoCode); Assert.Contains( await s.Server.PayTester.GetService <BTCPayWalletProvider>().GetWallet(walletId.CryptoCode) .GetUnspentCoins(x.AccountDerivation), coin => coin.OutPoint == spentOutpoint); }); await s.Server.ExplorerNode.GenerateAsync(1); s.GoToWallet(walletId, WalletsNavPages.Send); s.Driver.FindElement(By.Id("advancedSettings")).Click(); s.Driver.FindElement(By.Id("toggleInputSelection")).Click(); s.Driver.WaitForElement(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(); var inputSelectionSelect = s.Driver.FindElement(By.Name("SelectedInputs")); Assert.Single(inputSelectionSelect.FindElements(By.CssSelector("[selected]"))); var bob = new Key().PubKey.Hash.GetAddress(Network.RegTest); 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(); var happyText = happyElement.Text; var txid = Regex.Match(happyText, @"\((.*)\)").Groups[1].Value; tx = await s.Server.ExplorerNode.GetRawTransactionAsync(new uint256(txid)); Assert.Single(tx.Inputs); Assert.Equal(spentOutpoint, tx.Inputs[0].PrevOut); } }
public void SetCheckbox(SeleniumTester s, string inputName, bool value) { SetCheckbox(s.Driver.FindElement(By.Name(inputName)), value); }
public async Task CanManageWallet() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); s.RegisterNewUser(true); var storeId = s.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 s.GenerateWallet("BTC", "", true, false); //let's test quickly the receive wallet page s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.Driver.FindElement(By.Id("WalletSend")).Click(); s.Driver.ScrollTo(By.Id("SendMenu")); s.Driver.FindElement(By.Id("SendMenu")).ForceClick(); //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(); //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("vue-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.FindElement(By.CssSelector("button[value=generate-new-address]")).Click(); Assert.True(s.Driver.FindElement(By.ClassName("qr-container")).Displayed); Assert.Equal(receiveAddr, s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value")); //send money to addr and ensure it changed var sess = await s.Server.ExplorerClient.CreateWebsocketNotificationSessionAsync(); sess.ListenAllTrackedSource(); var nextEvent = sess.NextEventAsync(); s.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create(receiveAddr, Network.RegTest), Money.Parse("0.1")); await nextEvent; await Task.Delay(200); s.Driver.Navigate().Refresh(); s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click(); Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value")); receiveAddr = s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value"); //change the wallet and ensure old address is not there and generating a new one does not result in the prev one s.GoToStore(storeId.storeId); s.GenerateWallet("BTC", "", true, false); s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.Driver.FindElement(By.Id("WalletReceive")).Click(); s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click(); Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value")); var invoiceId = s.CreateInvoice(storeId.storeName); var invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId); var address = invoice.EntityToDTO().Addresses["BTC"]; //wallet should have been imported to bitcoin core wallet in watch only mode. var result = await s.Server.ExplorerNode.GetAddressInfoAsync(BitcoinAddress.Create(address, Network.RegTest)); Assert.True(result.IsWatchOnly); s.GoToStore(storeId.storeId); var mnemonic = s.GenerateWallet("BTC", "", true, true); //lets import and save private keys var root = mnemonic.DeriveExtKey(); invoiceId = s.CreateInvoice(storeId.storeName); invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId); address = invoice.EntityToDTO().Addresses["BTC"]; result = await s.Server.ExplorerNode.GetAddressInfoAsync(BitcoinAddress.Create(address, Network.RegTest)); //spendable from bitcoin core wallet! Assert.False(result.IsWatchOnly); var tx = s.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create(address, Network.RegTest), Money.Coins(3.0m)); s.Server.ExplorerNode.Generate(1); s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.ClickOnAllSideMenus(); // Make sure we can rescan, because we are admin! s.Driver.FindElement(By.Id("WalletRescan")).ForceClick(); 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("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(Mnemonic signingSource) { // Send to bob 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.CssSelector("button[value=seed]")).Click(); // Input the seed s.Driver.FindElement(By.Id("SeedOrKey")).SendKeys(signingSource.ToString() + 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); } SignWith(mnemonic); s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.Driver.FindElement(By.Id("WalletSend")).Click(); 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.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(); Assert.EndsWith("psbt", s.Driver.Url); s.Driver.FindElement(By.CssSelector("#OtherActions")).ForceClick(); s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick(); Assert.EndsWith("psbt/ready", s.Driver.Url); s.Driver.FindElement(By.CssSelector("button[value=broadcast]")).ForceClick(); Assert.Equal(walletTransactionLink, s.Driver.Url); var bip21 = invoice.EntityToDTO().CryptoInfo.First().PaymentUrls.BIP21; //let's make bip21 more interesting bip21 += "&label=Solid Snake&message=Snake? Snake? SNAAAAKE!"; var parsedBip21 = new BitcoinUrlBuilder(bip21, Network.RegTest); s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.Driver.FindElement(By.Id("WalletSend")).Click(); s.Driver.FindElement(By.Id("bip21parse")).Click(); s.Driver.SwitchTo().Alert().SendKeys(bip21); s.Driver.SwitchTo().Alert().Accept(); s.AssertHappyMessage(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); s.Driver.FindElement(By.Id("SettingsMenu")).ForceClick(); s.Driver.FindElement(By.CssSelector("button[value=view-seed]")).Click(); s.AssertHappyMessage(); Assert.Equal(mnemonic.ToString(), s.Driver.FindElements(By.ClassName("alert-success")).First().FindElement(By.TagName("code")).Text); } }
public async Task CanUseEthereum() { using var s = SeleniumTester.Create("ETHEREUM", true); s.Server.ActivateETH(); await s.StartAsync(); s.RegisterNewUser(true); IWebElement syncSummary = null; TestUtils.Eventually(() => { syncSummary = s.Driver.FindElement(By.Id("modalDialog")); Assert.True(syncSummary.Displayed); }); var web3Link = syncSummary.FindElement(By.LinkText("Configure Web3")); web3Link.Click(); s.Driver.FindElement(By.Id("Web3ProviderUrl")).SendKeys("https://ropsten-rpc.linkpool.io"); s.Driver.FindElement(By.Id("saveButton")).Click(); s.AssertHappyMessage(); TestUtils.Eventually(() => { s.Driver.Navigate().Refresh(); s.Driver.AssertElementNotFound(By.Id("modalDialog")); }); var store = s.CreateNewStore(); s.Driver.FindElement(By.LinkText("Ethereum")).Click(); var seed = new Mnemonic(Wordlist.English); s.Driver.FindElement(By.Id("ModifyETH")).Click(); s.Driver.FindElement(By.Id("Seed")).SendKeys(seed.ToString()); s.SetCheckbox(s.Driver.FindElement(By.Id("StoreSeed")), true); s.SetCheckbox(s.Driver.FindElement(By.Id("Enabled")), true); s.Driver.FindElement(By.Id("SaveButton")).Click(); s.AssertHappyMessage(); s.Driver.FindElement(By.Id("ModifyUSDT20")).Click(); s.Driver.FindElement(By.Id("Seed")).SendKeys(seed.ToString()); s.SetCheckbox(s.Driver.FindElement(By.Id("StoreSeed")), true); s.SetCheckbox(s.Driver.FindElement(By.Id("Enabled")), true); s.Driver.FindElement(By.Id("SaveButton")).Click(); s.AssertHappyMessage(); var invoiceId = s.CreateInvoice(store.storeName, 10); s.GoToInvoiceCheckout(invoiceId); var currencyDropdownButton = s.Driver.WaitForElement(By.ClassName("payment__currencies")); Assert.Contains("ETH", currencyDropdownButton.Text); s.Driver.FindElement(By.Id("copy-tab")).Click(); var ethAddress = s.Driver.FindElements(By.ClassName("copySectionBox")) .Single(element => element.FindElement(By.TagName("label")).Text .Contains("Address", StringComparison.InvariantCultureIgnoreCase)).FindElement(By.TagName("input")) .GetAttribute("value"); currencyDropdownButton.Click(); var elements = s.Driver.FindElement(By.ClassName("vex-content")).FindElements(By.ClassName("vexmenuitem")); Assert.Equal(2, elements.Count); elements.Single(element => element.Text.Contains("USDT20")).Click(); s.Driver.FindElement(By.Id("copy-tab")).Click(); var usdtAddress = s.Driver.FindElements(By.ClassName("copySectionBox")) .Single(element => element.FindElement(By.TagName("label")).Text .Contains("Address", StringComparison.InvariantCultureIgnoreCase)).FindElement(By.TagName("input")) .GetAttribute("value"); Assert.Equal(usdtAddress, ethAddress); }
public async Task CanCreateApiKeys() { //there are 2 ways to create api keys: //as a user through your profile //as an external application requesting an api key from a user using (var s = SeleniumTester.Create()) { await s.StartAsync(); var tester = s.Server; var user = tester.NewAccount(); user.GrantAccess(); await user.MakeAdmin(false); s.GoToLogin(); s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); s.GoToProfile(ManageNavPages.APIKeys); s.Driver.FindElement(By.Id("AddApiKey")).Click(); //not an admin, so this permission should not show Assert.DoesNotContain("btcpay.server.canmodifyserversettings", s.Driver.PageSource); await user.MakeAdmin(); s.Logout(); s.GoToLogin(); s.Login(user.RegisterDetails.Email, user.RegisterDetails.Password); s.GoToProfile(ManageNavPages.APIKeys); s.Driver.FindElement(By.Id("AddApiKey")).Click(); Assert.Contains("btcpay.server.canmodifyserversettings", s.Driver.PageSource); //server management should show now s.SetCheckbox(s, "btcpay.server.canmodifyserversettings", true); 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; //this api key has access to everything await TestApiAgainstAccessToken(superApiKey, tester, user, Policies.CanModifyServerSettings, Policies.CanModifyStoreSettings, Policies.CanViewProfile); 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; await TestApiAgainstAccessToken(serverOnlyApiKey, tester, user, Policies.CanModifyServerSettings); 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; await TestApiAgainstAccessToken(allStoreOnlyApiKey, tester, user, Policies.CanModifyStoreSettings); s.Driver.FindElement(By.Id("AddApiKey")).Click(); s.Driver.FindElement(By.CssSelector("button[value='btcpay.store.canmodifystoresettings:change-store-mode']")).Click(); //there should be a store already by default in the dropdown var dropdown = s.Driver.FindElement(By.Name("PermissionValues[2].SpecificStores[0]")); var option = dropdown.FindElement(By.TagName("option")); var storeId = option.GetAttribute("value"); option.Click(); s.Driver.FindElement(By.Id("Generate")).Click(); var selectiveStoreApiKey = s.AssertHappyMessage().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; await TestApiAgainstAccessToken(noPermissionsApiKey, tester, user); await Assert.ThrowsAnyAsync <HttpRequestException>(async() => { await TestApiAgainstAccessToken <bool>("incorrect key", $"{TestApiPath}/me/id", tester.PayTester.HttpClient); }); //let's test the authorized screen now //options for authorize are: //applicationName //redirect //permissions //strict //selectiveStores var authUrl = BTCPayServerClient.GenerateAuthorizeUri(tester.PayTester.ServerUri, new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }).ToString(); s.Driver.Navigate().GoToUrl(authUrl); s.Driver.PageSource.Contains("kukksappname"); Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant()); Assert.Equal("hidden", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant()); Assert.DoesNotContain("change-store-mode", s.Driver.PageSource); s.Driver.FindElement(By.Id("consent-yes")).Click(); var url = s.Driver.Url; IEnumerable <KeyValuePair <string, string> > results = url.Split("?").Last().Split("&") .Select(s1 => new KeyValuePair <string, string>(s1.Split("=")[0], s1.Split("=")[1])); var apiKeyRepo = s.Server.PayTester.GetService <APIKeyRepository>(); await TestApiAgainstAccessToken(results.Single(pair => pair.Key == "key").Value, tester, user, (await apiKeyRepo.GetKey(results.Single(pair => pair.Key == "key").Value)).GetBlob().Permissions); authUrl = BTCPayServerClient.GenerateAuthorizeUri(tester.PayTester.ServerUri, new[] { Policies.CanModifyStoreSettings, Policies.CanModifyServerSettings }, false, true).ToString(); s.Driver.Navigate().GoToUrl(authUrl); Assert.DoesNotContain("kukksappname", s.Driver.PageSource); Assert.Equal("checkbox", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.store.canmodifystoresettings")).GetAttribute("value").ToLowerInvariant()); Assert.Equal("checkbox", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("type").ToLowerInvariant()); Assert.Equal("true", s.Driver.FindElement(By.Id("btcpay.server.canmodifyserversettings")).GetAttribute("value").ToLowerInvariant()); s.SetCheckbox(s, "btcpay.server.canmodifyserversettings", false); Assert.Contains("change-store-mode", s.Driver.PageSource); s.Driver.FindElement(By.Id("consent-yes")).Click(); url = s.Driver.Url; results = url.Split("?").Last().Split("&") .Select(s1 => new KeyValuePair <string, string>(s1.Split("=")[0], s1.Split("=")[1])); await TestApiAgainstAccessToken(results.Single(pair => pair.Key == "key").Value, tester, user, (await apiKeyRepo.GetKey(results.Single(pair => pair.Key == "key").Value)).GetBlob().Permissions); } }
public async Task CanManageWallet() { using (var s = SeleniumTester.Create()) { await s.StartAsync(); s.RegisterNewUser(true); var storeId = s.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 s.GenerateWallet("BTC", "", true, false); //let's test quickly the receive wallet page s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.Driver.FindElement(By.Id("WalletSend")).Click(); s.Driver.ScrollTo(By.Id("SendMenu")); s.Driver.FindElement(By.Id("SendMenu")).ForceClick(); //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(); //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("vue-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.FindElement(By.CssSelector("button[value=generate-new-address]")).Click(); Assert.True(s.Driver.FindElement(By.ClassName("qr-container")).Displayed); Assert.Equal(receiveAddr, s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value")); //send money to addr and ensure it changed var sess = await s.Server.ExplorerClient.CreateWebsocketNotificationSessionAsync(); sess.ListenAllTrackedSource(); var nextEvent = sess.NextEventAsync(); s.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create(receiveAddr, Network.RegTest), Money.Parse("0.1")); await nextEvent; await Task.Delay(200); s.Driver.Navigate().Refresh(); s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click(); Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value")); receiveAddr = s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value"); //change the wallet and ensure old address is not there and generating a new one does not result in the prev one s.GoToStore(storeId.storeId); s.GenerateWallet("BTC", "", true, false); s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.Driver.FindElement(By.Id("WalletReceive")).Click(); s.Driver.FindElement(By.CssSelector("button[value=generate-new-address]")).Click(); Assert.NotEqual(receiveAddr, s.Driver.FindElement(By.Id("vue-address")).GetAttribute("value")); var invoiceId = s.CreateInvoice(storeId.storeId); var invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId); var address = invoice.EntityToDTO().Addresses["BTC"]; //wallet should have been imported to bitcoin core wallet in watch only mode. var result = await s.Server.ExplorerNode.GetAddressInfoAsync(BitcoinAddress.Create(address, Network.RegTest)); Assert.True(result.IsWatchOnly); s.GoToStore(storeId.storeId); var mnemonic = s.GenerateWallet("BTC", "", true, true); //lets import and save private keys var root = new Mnemonic(mnemonic).DeriveExtKey(); invoiceId = s.CreateInvoice(storeId.storeId); invoice = await s.Server.PayTester.InvoiceRepository.GetInvoice(invoiceId); address = invoice.EntityToDTO().Addresses["BTC"]; result = await s.Server.ExplorerNode.GetAddressInfoAsync(BitcoinAddress.Create(address, Network.RegTest)); //spendable from bitcoin core wallet! Assert.False(result.IsWatchOnly); var tx = s.Server.ExplorerNode.SendToAddress(BitcoinAddress.Create(address, Network.RegTest), Money.Coins(3.0m)); s.Server.ExplorerNode.Generate(1); s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.ClickOnAllSideMenus(); // Make sure we can rescan, because we are admin! s.Driver.FindElement(By.Id("WalletRescan")).ForceClick(); 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("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); s.Driver.FindElement(By.Id("Wallets")).Click(); s.Driver.FindElement(By.LinkText("Manage")).Click(); s.Driver.FindElement(By.Id("WalletSend")).Click(); var jack = new Key().PubKey.Hash.GetAddress(Network.RegTest); SetTransactionOutput(0, jack, 0.01m); s.Driver.ScrollTo(By.Id("SendMenu")); s.Driver.FindElement(By.Id("SendMenu")).ForceClick(); 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=broadcast]")).ForceClick(); Assert.Equal(walletTransactionLink, s.Driver.Url); } }