2020-06-29 04:44:35 +02:00
using System ;
2023-06-24 16:14:54 +02:00
using System.Collections.Generic ;
2021-04-13 10:36:49 +02:00
using System.Collections.ObjectModel ;
2020-06-28 10:55:27 +02:00
using System.Globalization ;
2019-05-12 10:13:26 +02:00
using System.Linq ;
2023-02-20 11:35:54 +01:00
using System.Net ;
2021-10-26 13:55:13 +02:00
using System.Net.Http ;
2023-12-06 01:17:58 +01:00
using System.Security.Cryptography ;
2020-11-06 12:42:26 +01:00
using System.Text ;
2019-10-12 13:35:30 +02:00
using System.Text.RegularExpressions ;
2021-10-18 05:37:59 +02:00
using System.Threading ;
2020-06-28 10:55:27 +02:00
using System.Threading.Tasks ;
2020-11-17 13:46:23 +01:00
using BTCPayServer.Abstractions.Models ;
2023-03-20 02:46:46 +01:00
using BTCPayServer.Client ;
2020-11-06 12:42:26 +01:00
using BTCPayServer.Client.Models ;
2020-03-25 07:59:30 +01:00
using BTCPayServer.Data ;
2021-10-18 05:37:59 +02:00
using BTCPayServer.Lightning ;
2023-12-06 01:17:58 +01:00
using BTCPayServer.NTag424 ;
2021-10-18 05:37:59 +02:00
using BTCPayServer.Payments ;
2021-04-17 06:29:50 +02:00
using BTCPayServer.Services ;
2021-10-26 13:55:13 +02:00
using BTCPayServer.Services.Invoices ;
2020-03-25 07:59:30 +01:00
using BTCPayServer.Services.Wallets ;
2021-12-24 09:27:00 +01:00
using BTCPayServer.Views.Manage ;
2020-09-16 08:54:09 +02:00
using BTCPayServer.Views.Server ;
2021-04-07 06:08:42 +02:00
using BTCPayServer.Views.Stores ;
2020-04-28 08:06:28 +02:00
using BTCPayServer.Views.Wallets ;
2023-12-06 01:17:58 +01:00
using ExchangeSharp ;
2021-10-26 13:55:13 +02:00
using LNURL ;
2021-02-12 04:21:29 +01:00
using Microsoft.AspNetCore.Identity ;
2020-06-24 03:34:09 +02:00
using Microsoft.EntityFrameworkCore ;
2020-06-28 10:55:27 +02:00
using NBitcoin ;
2020-11-06 12:42:26 +01:00
using NBitcoin.DataEncoders ;
2020-06-28 10:55:27 +02:00
using NBitcoin.Payment ;
2023-04-07 10:48:58 +02:00
using Newtonsoft.Json ;
2020-11-06 12:42:26 +01:00
using Newtonsoft.Json.Linq ;
2020-06-28 10:55:27 +02:00
using OpenQA.Selenium ;
2020-11-06 12:42:26 +01:00
using OpenQA.Selenium.Support.Extensions ;
using OpenQA.Selenium.Support.UI ;
2020-06-28 10:55:27 +02:00
using Xunit ;
using Xunit.Abstractions ;
2019-05-12 10:13:26 +02:00
namespace BTCPayServer.Tests
{
2019-05-13 11:42:20 +02:00
[Trait("Selenium", "Selenium")]
2021-11-23 05:57:45 +01:00
[Collection(nameof(NonParallelizableCollectionDefinition))]
2021-11-22 09:16:08 +01:00
public class ChromeTests : UnitTestBase
2019-05-12 10:13:26 +02:00
{
2021-01-22 17:49:26 +01:00
private const int TestTimeout = TestUtils . TestTimeout ;
2021-11-22 09:16:08 +01:00
public ChromeTests ( ITestOutputHelper helper ) : base ( helper )
2019-05-12 10:13:26 +02:00
{
}
2019-10-06 15:24:28 +02:00
[Fact(Timeout = TestTimeout)]
2019-10-07 09:04:25 +02:00
public async Task CanNavigateServerSettings ( )
2019-05-13 10:59:15 +02:00
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
2023-07-19 15:21:16 +02:00
s . GoToHome ( ) ;
s . GoToServer ( ) ;
2022-01-14 09:50:29 +01:00
s . Driver . AssertNoError ( ) ;
s . ClickOnAllSectionLinks ( ) ;
2023-07-19 15:21:16 +02:00
s . GoToServer ( ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . LinkText ( "Services" ) ) . Click ( ) ;
TestLogs . LogInformation ( "Let's check if we can access the logs" ) ;
s . Driver . FindElement ( By . LinkText ( "Logs" ) ) . Click ( ) ;
s . Driver . FindElement ( By . PartialLinkText ( ".log" ) ) . Click ( ) ;
Assert . Contains ( "Starting listening NBXplorer" , s . Driver . PageSource ) ;
s . Driver . Quit ( ) ;
2019-12-24 10:11:21 +01:00
}
2023-01-06 14:18:07 +01:00
2022-11-25 02:42:55 +01:00
[Fact(Timeout = TestTimeout)]
public async Task CanUseForms ( )
{
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
s . CreateNewStore ( ) ;
s . GenerateWallet ( isHotWallet : true ) ;
2023-01-06 14:18:07 +01:00
2022-11-25 02:42:55 +01:00
// Point Of Sale
2024-03-11 11:04:41 +01:00
var appName = $"PoS-{Guid.NewGuid().ToString()[..21]}" ;
2023-03-18 22:36:26 +01:00
s . Driver . FindElement ( By . Id ( "StoreNav-CreatePointOfSale" ) ) . Click ( ) ;
2024-03-11 11:04:41 +01:00
s . Driver . FindElement ( By . Id ( "AppName" ) ) . SendKeys ( appName ) ;
2022-11-25 02:42:55 +01:00
s . Driver . FindElement ( By . Id ( "Create" ) ) . Click ( ) ;
Assert . Contains ( "App successfully created" , s . FindAlertMessage ( ) . Text ) ;
2023-01-06 14:18:07 +01:00
2022-11-25 02:42:55 +01:00
new SelectElement ( s . Driver . FindElement ( By . Id ( "FormId" ) ) ) . SelectByValue ( "Email" ) ;
s . Driver . FindElement ( By . Id ( "SaveSettings" ) ) . Click ( ) ;
Assert . Contains ( "App updated" , s . FindAlertMessage ( ) . Text ) ;
s . Driver . FindElement ( By . Id ( "ViewApp" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2024-02-21 14:41:21 +01:00
2022-11-25 02:42:55 +01:00
s . Driver . FindElement ( By . CssSelector ( "button[type='submit']" ) ) . Click ( ) ;
2023-01-06 14:18:07 +01:00
2022-11-25 02:42:55 +01:00
Assert . Contains ( "Enter your email" , s . Driver . PageSource ) ;
s . Driver . FindElement ( By . Name ( "buyerEmail" ) ) . SendKeys ( "aa@aa.com" ) ;
s . Driver . FindElement ( By . CssSelector ( "input[type='submit']" ) ) . Click ( ) ;
2023-01-06 14:18:07 +01:00
2022-11-25 02:42:55 +01:00
s . PayInvoice ( true ) ;
var invoiceId = s . Driver . Url [ ( s . Driver . Url . LastIndexOf ( "/" , StringComparison . Ordinal ) + 1 ) . . ] ;
2023-11-20 02:45:43 +01:00
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2024-02-21 14:41:21 +01:00
2022-11-25 02:42:55 +01:00
s . GoToInvoice ( invoiceId ) ;
Assert . Contains ( "aa@aa.com" , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
2022-11-25 02:42:55 +01:00
// Payment Request
s . Driver . FindElement ( By . Id ( "StoreNav-PaymentRequests" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "CreatePaymentRequest" ) ) . Click ( ) ;
2023-12-01 10:50:05 +01:00
Thread . Sleep ( 10000 ) ;
2022-11-25 02:42:55 +01:00
s . Driver . FindElement ( By . Id ( "Title" ) ) . SendKeys ( "Pay123" ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( "700" ) ;
new SelectElement ( s . Driver . FindElement ( By . Id ( "FormId" ) ) ) . SelectByValue ( "Email" ) ;
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
2023-01-06 14:18:07 +01:00
2022-11-25 02:42:55 +01:00
s . Driver . FindElement ( By . XPath ( "//a[starts-with(@id, 'Edit-')]" ) ) . Click ( ) ;
var editUrl = s . Driver . Url ;
2024-02-21 14:41:21 +01:00
2022-11-25 02:42:55 +01:00
s . Driver . FindElement ( By . Id ( "ViewPaymentRequest" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2024-02-21 14:41:21 +01:00
2022-11-25 02:42:55 +01:00
s . Driver . FindElement ( By . CssSelector ( "[data-test='form-button']" ) ) . Click ( ) ;
Assert . Contains ( "Enter your email" , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
2022-11-25 02:42:55 +01:00
s . Driver . FindElement ( By . Name ( "buyerEmail" ) ) . SendKeys ( "aa@aa.com" ) ;
s . Driver . FindElement ( By . CssSelector ( "input[type='submit']" ) ) . Click ( ) ;
2023-04-24 11:04:46 +02:00
invoiceId = s . Driver . Url . Split ( '/' ) . Last ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2024-02-21 14:41:21 +01:00
2022-11-25 02:42:55 +01:00
s . Driver . Navigate ( ) . GoToUrl ( editUrl ) ;
Assert . Contains ( "aa@aa.com" , s . Driver . PageSource ) ;
2023-04-24 11:04:46 +02:00
var invoice = await s . Server . PayTester . GetService < InvoiceRepository > ( ) . GetInvoice ( invoiceId ) ;
Assert . Equal ( "aa@aa.com" , invoice . Metadata . BuyerEmail ) ;
2023-02-20 11:35:54 +01:00
//Custom Forms
s . GoToStore ( StoreNavPages . Forms ) ;
Assert . Contains ( "There are no forms yet." , s . Driver . PageSource ) ;
s . Driver . FindElement ( By . Id ( "CreateForm" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Name ( "Name" ) ) . SendKeys ( "Custom Form 1" ) ;
2023-05-22 13:30:28 +02:00
s . Driver . FindElement ( By . Id ( "ApplyEmailTemplate" ) ) . Click ( ) ;
2024-02-21 14:41:21 +01:00
2023-05-22 13:30:28 +02:00
s . Driver . FindElement ( By . Id ( "CodeTabButton" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "CodeTabPane" ) ) ;
2024-02-21 14:41:21 +01:00
2023-05-22 13:30:28 +02:00
var config = s . Driver . FindElement ( By . Name ( "FormConfig" ) ) . GetAttribute ( "value" ) ;
Assert . Contains ( "buyerEmail" , config ) ;
2024-02-21 14:41:21 +01:00
2023-02-20 11:35:54 +01:00
s . Driver . FindElement ( By . Name ( "FormConfig" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Name ( "FormConfig" ) )
2023-05-22 13:30:28 +02:00
. SendKeys ( config . Replace ( "Enter your email" , "CustomFormInputTest" ) ) ;
2023-02-20 11:35:54 +01:00
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "ViewForm" ) ) . Click ( ) ;
var formurl = s . Driver . Url ;
Assert . Contains ( "CustomFormInputTest" , s . Driver . PageSource ) ;
s . Driver . FindElement ( By . Name ( "buyerEmail" ) ) . SendKeys ( "aa@aa.com" ) ;
s . Driver . FindElement ( By . CssSelector ( "input[type='submit']" ) ) . Click ( ) ;
2023-12-20 14:00:08 +01:00
s . PayInvoice ( true , 0.001 m ) ;
2023-02-20 11:35:54 +01:00
var result = await s . Server . PayTester . HttpClient . GetAsync ( formurl ) ;
Assert . Equal ( HttpStatusCode . NotFound , result . StatusCode ) ;
s . GoToHome ( ) ;
s . GoToStore ( StoreNavPages . Forms ) ;
Assert . Contains ( "Custom Form 1" , s . Driver . PageSource ) ;
s . Driver . FindElement ( By . LinkText ( "Remove" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "ConfirmInput" ) ) . SendKeys ( "DELETE" ) ;
s . Driver . FindElement ( By . Id ( "ConfirmContinue" ) ) . Click ( ) ;
2023-04-10 04:07:03 +02:00
2023-02-20 11:35:54 +01:00
Assert . DoesNotContain ( "Custom Form 1" , s . Driver . PageSource ) ;
s . Driver . FindElement ( By . Id ( "CreateForm" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Name ( "Name" ) ) . SendKeys ( "Custom Form 2" ) ;
2023-05-22 13:30:28 +02:00
s . Driver . FindElement ( By . Id ( "ApplyEmailTemplate" ) ) . Click ( ) ;
2024-02-21 14:41:21 +01:00
2023-05-22 13:30:28 +02:00
s . Driver . FindElement ( By . Id ( "CodeTabButton" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "CodeTabPane" ) ) ;
2024-02-21 14:41:21 +01:00
2023-02-20 11:35:54 +01:00
s . Driver . SetCheckbox ( By . Name ( "Public" ) , true ) ;
s . Driver . FindElement ( By . Name ( "FormConfig" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Name ( "FormConfig" ) )
2023-05-22 13:30:28 +02:00
. SendKeys ( config . Replace ( "Enter your email" , "CustomFormInputTest2" ) ) ;
2023-02-20 11:35:54 +01:00
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "ViewForm" ) ) . Click ( ) ;
formurl = s . Driver . Url ;
result = await s . Server . PayTester . HttpClient . GetAsync ( formurl ) ;
Assert . NotEqual ( HttpStatusCode . NotFound , result . StatusCode ) ;
2023-04-10 04:07:03 +02:00
2023-02-20 11:35:54 +01:00
s . GoToHome ( ) ;
s . GoToStore ( StoreNavPages . Forms ) ;
Assert . Contains ( "Custom Form 2" , s . Driver . PageSource ) ;
2023-04-10 04:07:03 +02:00
2023-02-20 11:35:54 +01:00
s . Driver . FindElement ( By . LinkText ( "Custom Form 2" ) ) . Click ( ) ;
2023-04-10 04:07:03 +02:00
2023-02-20 11:35:54 +01:00
s . Driver . FindElement ( By . Name ( "Name" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Name ( "Name" ) ) . SendKeys ( "Custom Form 3" ) ;
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
s . GoToStore ( StoreNavPages . Forms ) ;
Assert . Contains ( "Custom Form 3" , s . Driver . PageSource ) ;
2023-04-10 04:07:03 +02:00
2023-02-20 11:35:54 +01:00
s . Driver . FindElement ( By . Id ( "StoreNav-PaymentRequests" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "CreatePaymentRequest" ) ) . Click ( ) ;
2023-04-10 04:07:03 +02:00
Assert . Equal ( 4 , new SelectElement ( s . Driver . FindElement ( By . Id ( "FormId" ) ) ) . Options . Count ) ;
2022-11-25 02:42:55 +01:00
}
2023-01-06 14:18:07 +01:00
2022-02-10 04:24:28 +01:00
[Fact(Timeout = TestTimeout)]
public async Task CanUseCPFP ( )
{
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
s . CreateNewStore ( ) ;
s . GenerateWallet ( isHotWallet : true ) ;
await s . FundStoreWallet ( ) ;
for ( int i = 0 ; i < 3 ; i + + )
{
s . CreateInvoice ( ) ;
s . GoToInvoiceCheckout ( ) ;
s . PayInvoice ( ) ;
s . GoToInvoices ( s . StoreId ) ;
}
// Let's CPFP from the invoices page
2023-11-02 08:12:28 +01:00
s . Driver . SetCheckbox ( By . CssSelector ( ".mass-action-select-all" ) , true ) ;
2022-02-10 04:24:28 +01:00
s . Driver . FindElement ( By . Id ( "BumpFee" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "BroadcastTransaction" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
Assert . Contains ( $"/stores/{s.StoreId}/invoices" , s . Driver . Url ) ;
// CPFP again should fail because all invoices got bumped
s . GoToInvoices ( ) ;
2023-11-02 08:12:28 +01:00
s . Driver . SetCheckbox ( By . CssSelector ( ".mass-action-select-all" ) , true ) ;
2022-02-10 04:24:28 +01:00
s . Driver . FindElement ( By . Id ( "BumpFee" ) ) . Click ( ) ;
Assert . Contains ( $"/stores/{s.StoreId}/invoices" , s . Driver . Url ) ;
2022-07-04 06:20:08 +02:00
Assert . Contains ( "any UTXO available" , s . FindAlertMessage ( StatusMessageModel . StatusSeverity . Error ) . Text ) ;
2022-02-10 04:24:28 +01:00
// But we should be able to bump from the wallet's page
s . GoToWallet ( navPages : WalletsNavPages . Transactions ) ;
2023-11-02 08:12:28 +01:00
s . Driver . SetCheckbox ( By . CssSelector ( ".mass-action-select-all" ) , true ) ;
2022-02-10 04:24:28 +01:00
s . Driver . FindElement ( By . Id ( "BumpFee" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "BroadcastTransaction" ) ) . Click ( ) ;
Assert . Contains ( $"/wallets/{s.WalletId}" , s . Driver . Url ) ;
2022-07-04 06:20:08 +02:00
Assert . Contains ( "Transaction broadcasted successfully" , s . FindAlertMessage ( ) . Text ) ;
2022-02-10 04:24:28 +01:00
}
2019-12-24 10:11:21 +01:00
[Fact(Timeout = TestTimeout)]
[Trait("Lightning", "Lightning")]
public async Task CanUseLndSeedBackup ( )
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
s . Server . ActivateLightning ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
2023-07-19 15:21:16 +02:00
s . GoToHome ( ) ;
s . GoToServer ( ) ;
2022-01-14 09:50:29 +01:00
s . Driver . AssertNoError ( ) ;
s . Driver . FindElement ( By . LinkText ( "Services" ) ) . Click ( ) ;
TestLogs . LogInformation ( "Let's if we can access LND's seed" ) ;
Assert . Contains ( "server/services/lndseedbackup/BTC" , s . Driver . PageSource ) ;
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/services/lndseedbackup/BTC" ) ) ;
s . Driver . FindElement ( By . Id ( "details" ) ) . Click ( ) ;
var seedEl = s . Driver . FindElement ( By . Id ( "Seed" ) ) ;
Assert . True ( seedEl . Displayed ) ;
Assert . Contains ( "about over million" , seedEl . Text , StringComparison . OrdinalIgnoreCase ) ;
var passEl = s . Driver . FindElement ( By . Id ( "WalletPassword" ) ) ;
Assert . True ( passEl . Displayed ) ;
Assert . Contains ( passEl . Text , "hellorockstar" , StringComparison . OrdinalIgnoreCase ) ;
s . Driver . FindElement ( By . Id ( "delete" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "ConfirmInput" ) ) . SendKeys ( "DELETE" ) ;
s . Driver . FindElement ( By . Id ( "ConfirmContinue" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
seedEl = s . Driver . FindElement ( By . Id ( "Seed" ) ) ;
Assert . Contains ( "Seed removed" , seedEl . Text , StringComparison . OrdinalIgnoreCase ) ;
2019-05-13 10:59:15 +02:00
}
2021-02-12 04:21:29 +01:00
[Fact(Timeout = TestTimeout)]
[Trait("Selenium", "Selenium")]
public async Task CanChangeUserMail ( )
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
2021-02-12 04:21:29 +01:00
2022-01-14 09:50:29 +01:00
var tester = s . Server ;
var u1 = tester . NewAccount ( ) ;
await u1 . GrantAccessAsync ( ) ;
await u1 . MakeAdmin ( false ) ;
var u2 = tester . NewAccount ( ) ;
await u2 . GrantAccessAsync ( ) ;
await u2 . MakeAdmin ( false ) ;
s . GoToLogin ( ) ;
2022-02-07 13:18:22 +01:00
s . LogIn ( u1 . RegisterDetails . Email , u1 . RegisterDetails . Password ) ;
2022-01-14 09:50:29 +01:00
s . GoToProfile ( ) ;
s . Driver . FindElement ( By . Id ( "Email" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Email" ) ) . SendKeys ( u2 . RegisterDetails . Email ) ;
s . Driver . FindElement ( By . Id ( "save" ) ) . Click ( ) ;
Assert . Contains ( "The email address is already in use with an other account." ,
s . FindAlertMessage ( StatusMessageModel . StatusSeverity . Error ) . Text ) ;
s . GoToProfile ( ) ;
s . Driver . FindElement ( By . Id ( "Email" ) ) . Clear ( ) ;
var changedEmail = Guid . NewGuid ( ) + "@lol.com" ;
s . Driver . FindElement ( By . Id ( "Email" ) ) . SendKeys ( changedEmail ) ;
s . Driver . FindElement ( By . Id ( "save" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
var manager = tester . PayTester . GetService < UserManager < ApplicationUser > > ( ) ;
Assert . NotNull ( await manager . FindByNameAsync ( changedEmail ) ) ;
Assert . NotNull ( await manager . FindByEmailAsync ( changedEmail ) ) ;
2021-02-12 04:21:29 +01:00
}
2019-10-06 15:24:28 +02:00
[Fact(Timeout = TestTimeout)]
2019-10-07 09:04:25 +02:00
public async Task NewUserLogin ( )
2019-05-12 10:13:26 +02:00
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
//Register & Log Out
var email = s . RegisterNewUser ( ) ;
2023-07-19 15:21:16 +02:00
s . GoToHome ( ) ;
2022-01-14 09:50:29 +01:00
s . Logout ( ) ;
s . Driver . AssertNoError ( ) ;
Assert . Contains ( "/login" , s . Driver . Url ) ;
2022-01-14 12:16:28 +01:00
s . GoToUrl ( "/account" ) ;
Assert . Contains ( "ReturnUrl=%2Faccount" , s . Driver . Url ) ;
2022-01-14 09:50:29 +01:00
// 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
2022-01-14 12:16:28 +01:00
Assert . EndsWith ( "/account" , s . Driver . Url ) ;
2022-01-14 09:50:29 +01:00
// Should not be able to reach server settings
s . GoToUrl ( "/server/users" ) ;
Assert . Contains ( "ReturnUrl=%2Fserver%2Fusers" , s . Driver . Url ) ;
s . GoToHome ( ) ;
//Change Password & Log Out
2024-02-28 12:43:18 +01:00
var newPassword = "abc???" ;
2022-01-14 09:50:29 +01:00
s . GoToProfile ( ManageNavPages . ChangePassword ) ;
s . Driver . FindElement ( By . Id ( "OldPassword" ) ) . SendKeys ( "123456" ) ;
2024-02-28 12:43:18 +01:00
s . Driver . FindElement ( By . Id ( "NewPassword" ) ) . SendKeys ( newPassword ) ;
s . Driver . FindElement ( By . Id ( "ConfirmPassword" ) ) . SendKeys ( newPassword ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "UpdatePassword" ) ) . Click ( ) ;
s . Logout ( ) ;
s . Driver . AssertNoError ( ) ;
//Log In With New Password
s . Driver . FindElement ( By . Id ( "Email" ) ) . SendKeys ( email ) ;
2024-02-28 12:43:18 +01:00
s . Driver . FindElement ( By . Id ( "Password" ) ) . SendKeys ( newPassword ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "LoginButton" ) ) . Click ( ) ;
2023-07-19 15:21:16 +02:00
s . GoToHome ( ) ;
2022-01-14 09:50:29 +01:00
s . GoToProfile ( ) ;
s . ClickOnAllSectionLinks ( ) ;
//let's test invite link
s . Logout ( ) ;
s . GoToRegister ( ) ;
s . RegisterNewUser ( true ) ;
2023-07-19 15:21:16 +02:00
s . GoToHome ( ) ;
2022-01-14 09:50:29 +01:00
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 . FindAlertMessage ( ) . 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" ) ) ;
2024-03-19 14:58:33 +01:00
Assert . Equal ( "Create Account" , s . Driver . FindElement ( By . CssSelector ( "h4" ) ) . Text ) ;
2024-02-28 12:43:18 +01:00
Assert . Contains ( "Invitation accepted. Please set your password." , s . FindAlertMessage ( StatusMessageModel . StatusSeverity . Info ) . Text ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "Password" ) ) . SendKeys ( "123456" ) ;
s . Driver . FindElement ( By . Id ( "ConfirmPassword" ) ) . SendKeys ( "123456" ) ;
s . Driver . FindElement ( By . Id ( "SetPassword" ) ) . Click ( ) ;
2024-03-19 14:58:33 +01:00
Assert . Contains ( "Account successfully created." , s . FindAlertMessage ( ) . Text ) ;
2024-02-28 12:43:18 +01:00
2022-01-14 09:50:29 +01:00
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
2023-07-19 15:21:16 +02:00
s . GoToHome ( ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "mainNav" ) ) ;
//let's test delete user quickly while we're at it
s . GoToProfile ( ) ;
s . Driver . FindElement ( By . Id ( "delete-user" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "ConfirmInput" ) ) . SendKeys ( "DELETE" ) ;
s . Driver . FindElement ( By . Id ( "ConfirmContinue" ) ) . Click ( ) ;
Assert . Contains ( "/login" , s . Driver . Url ) ;
2019-05-12 10:13:26 +02:00
}
2019-05-14 16:33:46 +02:00
2024-01-31 06:45:54 +01:00
[Fact(Timeout = TestTimeout)]
public async Task CanRequireApprovalForNewAccounts ( )
{
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
var settings = s . Server . PayTester . GetService < SettingsRepository > ( ) ;
var policies = await settings . GetSettingAsync < PoliciesSettings > ( ) ? ? new PoliciesSettings ( ) ;
Assert . True ( policies . EnableRegistration ) ;
Assert . False ( policies . RequiresUserApproval ) ;
// Register admin and adapt policies
s . RegisterNewUser ( true ) ;
var admin = s . AsTestAccount ( ) ;
s . GoToHome ( ) ;
s . GoToServer ( ServerNavPages . Policies ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "EnableRegistration" ) ) . Selected ) ;
Assert . False ( s . Driver . FindElement ( By . Id ( "RequiresUserApproval" ) ) . Selected ) ;
s . Driver . FindElement ( By . Id ( "RequiresUserApproval" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
Assert . Contains ( "Policies updated successfully" , s . FindAlertMessage ( ) . Text ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "RequiresUserApproval" ) ) . Selected ) ;
// Ensure there is no unread notification yet
s . Driver . ElementDoesNotExist ( By . Id ( "NotificationsBadge" ) ) ;
s . Logout ( ) ;
// Register user and try to log in
s . GoToRegister ( ) ;
s . RegisterNewUser ( ) ;
s . Driver . AssertNoError ( ) ;
Assert . Contains ( "Account created. The new account requires approval by an admin before you can log in" , s . FindAlertMessage ( ) . Text ) ;
Assert . Contains ( "/login" , s . Driver . Url ) ;
var unapproved = s . AsTestAccount ( ) ;
s . LogIn ( unapproved . RegisterDetails . Email , unapproved . RegisterDetails . Password ) ;
Assert . Contains ( "Your user account requires approval by an admin before you can log in" , s . FindAlertMessage ( StatusMessageModel . StatusSeverity . Warning ) . Text ) ;
Assert . Contains ( "/login" , s . Driver . Url ) ;
// Login with admin
s . GoToLogin ( ) ;
s . LogIn ( admin . RegisterDetails . Email , admin . RegisterDetails . Password ) ;
s . GoToHome ( ) ;
// Check notification
TestUtils . Eventually ( ( ) = > Assert . Equal ( "1" , s . Driver . FindElement ( By . Id ( "NotificationsBadge" ) ) . Text ) ) ;
s . Driver . FindElement ( By . Id ( "NotificationsHandle" ) ) . Click ( ) ;
Assert . Matches ( $"New user {unapproved.RegisterDetails.Email} requires approval" , s . Driver . FindElement ( By . CssSelector ( "#NotificationsList .notification" ) ) . Text ) ;
s . Driver . FindElement ( By . Id ( "NotificationsMarkAllAsSeen" ) ) . Click ( ) ;
// Reset approval policy
s . GoToServer ( ServerNavPages . Policies ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "EnableRegistration" ) ) . Selected ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "RequiresUserApproval" ) ) . Selected ) ;
s . Driver . FindElement ( By . Id ( "RequiresUserApproval" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
Assert . Contains ( "Policies updated successfully" , s . FindAlertMessage ( ) . Text ) ;
Assert . False ( s . Driver . FindElement ( By . Id ( "RequiresUserApproval" ) ) . Selected ) ;
// Check user create view does not have approval checkbox
s . GoToServer ( ServerNavPages . Users ) ;
s . Driver . FindElement ( By . Id ( "CreateUser" ) ) . Click ( ) ;
s . Driver . ElementDoesNotExist ( By . Id ( "Approved" ) ) ;
s . Logout ( ) ;
// Still requires approval for user who registered before
s . GoToLogin ( ) ;
s . LogIn ( unapproved . RegisterDetails . Email , unapproved . RegisterDetails . Password ) ;
Assert . Contains ( "Your user account requires approval by an admin before you can log in" , s . FindAlertMessage ( StatusMessageModel . StatusSeverity . Warning ) . Text ) ;
Assert . Contains ( "/login" , s . Driver . Url ) ;
// New user can register and gets in without approval
s . GoToRegister ( ) ;
s . RegisterNewUser ( ) ;
s . Driver . AssertNoError ( ) ;
Assert . DoesNotContain ( "/login" , s . Driver . Url ) ;
var autoApproved = s . AsTestAccount ( ) ;
s . CreateNewStore ( ) ;
s . Logout ( ) ;
// Login with admin and check list
s . GoToLogin ( ) ;
s . LogIn ( admin . RegisterDetails . Email , admin . RegisterDetails . Password ) ;
s . GoToHome ( ) ;
// No notification this time
s . Driver . ElementDoesNotExist ( By . Id ( "NotificationsBadge" ) ) ;
// Check users list
s . GoToServer ( ServerNavPages . Users ) ;
2024-02-23 09:51:41 +01:00
var rows = s . Driver . FindElements ( By . CssSelector ( "#UsersList tr.user-overview-row" ) ) ;
2024-01-31 06:45:54 +01:00
Assert . True ( rows . Count > = 3 ) ;
// Check user which didn't require approval
s . Driver . FindElement ( By . Id ( "SearchTerm" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "SearchTerm" ) ) . SendKeys ( autoApproved . RegisterDetails . Email ) ;
s . Driver . FindElement ( By . Id ( "SearchTerm" ) ) . SendKeys ( Keys . Enter ) ;
2024-02-23 09:51:41 +01:00
rows = s . Driver . FindElements ( By . CssSelector ( "#UsersList tr.user-overview-row" ) ) ;
2024-01-31 06:45:54 +01:00
Assert . Single ( rows ) ;
Assert . Contains ( autoApproved . RegisterDetails . Email , rows . First ( ) . Text ) ;
2024-02-23 09:51:41 +01:00
s . Driver . ElementDoesNotExist ( By . CssSelector ( "#UsersList tr.user-overview-row:first-child .user-approved" ) ) ;
2024-01-31 06:45:54 +01:00
// Edit view does not contain approve toggle
2024-02-23 09:51:41 +01:00
s . Driver . FindElement ( By . CssSelector ( "#UsersList tr.user-overview-row:first-child .user-edit" ) ) . Click ( ) ;
2024-01-31 06:45:54 +01:00
s . Driver . ElementDoesNotExist ( By . Id ( "Approved" ) ) ;
// Check user which still requires approval
s . GoToServer ( ServerNavPages . Users ) ;
s . Driver . FindElement ( By . Id ( "SearchTerm" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "SearchTerm" ) ) . SendKeys ( unapproved . RegisterDetails . Email ) ;
s . Driver . FindElement ( By . Id ( "SearchTerm" ) ) . SendKeys ( Keys . Enter ) ;
2024-02-23 09:51:41 +01:00
rows = s . Driver . FindElements ( By . CssSelector ( "#UsersList tr.user-overview-row" ) ) ;
2024-01-31 06:45:54 +01:00
Assert . Single ( rows ) ;
Assert . Contains ( unapproved . RegisterDetails . Email , rows . First ( ) . Text ) ;
2024-02-23 09:51:41 +01:00
Assert . Contains ( "Pending Approval" , s . Driver . FindElement ( By . CssSelector ( "#UsersList tr.user-overview-row:first-child .user-status" ) ) . Text ) ;
2024-01-31 06:45:54 +01:00
// Approve user
2024-02-23 09:51:41 +01:00
s . Driver . FindElement ( By . CssSelector ( "#UsersList tr.user-overview-row:first-child .user-edit" ) ) . Click ( ) ;
2024-01-31 06:45:54 +01:00
s . Driver . FindElement ( By . Id ( "Approved" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "SaveUser" ) ) . Click ( ) ;
Assert . Contains ( "User successfully updated" , s . FindAlertMessage ( ) . Text ) ;
// Check list again
s . GoToServer ( ServerNavPages . Users ) ;
Assert . Contains ( unapproved . RegisterDetails . Email , s . Driver . FindElement ( By . Id ( "SearchTerm" ) ) . GetAttribute ( "value" ) ) ;
2024-02-23 09:51:41 +01:00
rows = s . Driver . FindElements ( By . CssSelector ( "#UsersList tr.user-overview-row" ) ) ;
2024-01-31 06:45:54 +01:00
Assert . Single ( rows ) ;
Assert . Contains ( unapproved . RegisterDetails . Email , rows . First ( ) . Text ) ;
2024-02-23 09:51:41 +01:00
Assert . Contains ( "Active" , s . Driver . FindElement ( By . CssSelector ( "#UsersList tr.user-overview-row:first-child .user-status" ) ) . Text ) ;
2024-01-31 06:45:54 +01:00
// Finally, login user that needed approval
s . Logout ( ) ;
s . GoToLogin ( ) ;
s . LogIn ( unapproved . RegisterDetails . Email , unapproved . RegisterDetails . Password ) ;
s . Driver . AssertNoError ( ) ;
Assert . DoesNotContain ( "/login" , s . Driver . Url ) ;
s . CreateNewStore ( ) ;
}
2019-10-06 15:24:28 +02:00
[Fact(Timeout = TestTimeout)]
2019-09-06 09:51:49 +02:00
public async Task CanUseSSHService ( )
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
var settings = s . Server . PayTester . GetService < SettingsRepository > ( ) ;
var policies = await settings . GetSettingAsync < PoliciesSettings > ( ) ? ? new PoliciesSettings ( ) ;
policies . DisableSSHService = false ;
await settings . UpdateSetting ( policies ) ;
s . RegisterNewUser ( isAdmin : true ) ;
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/services" ) ) ;
Assert . Contains ( "server/services/ssh" , s . Driver . PageSource ) ;
using ( var client = await s . Server . PayTester . GetService < Configuration . BTCPayServerOptions > ( ) . SSHSettings
. ConnectAsync ( ) )
2019-09-06 09:51:49 +02:00
{
2022-01-14 09:50:29 +01:00
var result = await client . RunBash ( "echo hello" ) ;
Assert . Equal ( string . Empty , result . Error ) ;
Assert . Equal ( "hello\n" , result . Output ) ;
Assert . Equal ( 0 , result . ExitStatus ) ;
2019-09-06 09:51:49 +02:00
}
2022-01-14 09:50:29 +01:00
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/services/ssh" ) ) ;
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" ) ) . Click ( ) ;
s . Driver . AssertNoError ( ) ;
var text = s . Driver . FindElement ( By . Id ( "SSHKeyFileContent" ) ) . Text ;
// Browser replace \n to \r\n, so it is hard to compare exactly what we want
Assert . Contains ( "tes't" , text ) ;
Assert . Contains ( "test2" , text ) ;
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" ) ) . Click ( ) ;
text = s . Driver . FindElement ( By . Id ( "SSHKeyFileContent" ) ) . Text ;
Assert . DoesNotContain ( "test2" , text ) ;
// Let's try to disable it now
s . Driver . FindElement ( By . Id ( "disable" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "ConfirmInput" ) ) . SendKeys ( "DISABLE" ) ;
s . Driver . FindElement ( By . Id ( "ConfirmContinue" ) ) . Click ( ) ;
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/services/ssh" ) ) ;
Assert . True ( s . Driver . PageSource . Contains ( "404 - Page not found" , StringComparison . OrdinalIgnoreCase ) ) ;
policies = await settings . GetSettingAsync < PoliciesSettings > ( ) ;
Assert . True ( policies . DisableSSHService ) ;
policies . DisableSSHService = false ;
await settings . UpdateSetting ( policies ) ;
2019-09-06 09:51:49 +02:00
}
2020-10-05 09:39:49 +02:00
[Fact(Timeout = TestTimeout)]
public async Task CanSetupEmailServer ( )
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
2022-01-18 02:19:27 +01:00
s . RegisterNewUser ( true ) ;
2023-12-01 10:50:05 +01:00
s . CreateNewStore ( ) ;
2024-01-26 10:28:50 +01:00
// Ensure empty server settings
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/emails" ) ) ;
s . Driver . FindElement ( By . Id ( "Settings_Login" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Settings_Password" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Settings_From" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Save" ) ) . Submit ( ) ;
2023-12-01 10:50:05 +01:00
// Store Emails without server fallback
s . GoToStore ( StoreNavPages . Emails ) ;
2024-01-26 10:28:50 +01:00
s . Driver . ElementDoesNotExist ( By . Id ( "UseCustomSMTP" ) ) ;
2023-12-01 10:50:05 +01:00
s . Driver . FindElement ( By . Id ( "ConfigureEmailRules" ) ) . Click ( ) ;
Assert . Contains ( "You need to configure email settings before this feature works" , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
2022-06-22 05:05:32 +02:00
// Server Emails
2022-01-14 09:50:29 +01:00
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/emails" ) ) ;
if ( s . Driver . PageSource . Contains ( "Configured" ) )
2020-10-05 09:39:49 +02:00
{
2022-01-18 02:19:27 +01:00
s . Driver . FindElement ( By . Id ( "ResetPassword" ) ) . Submit ( ) ;
2022-01-14 09:50:29 +01:00
s . FindAlertMessage ( ) ;
2020-10-05 09:39:49 +02:00
}
2022-01-14 09:50:29 +01:00
CanSetupEmailCore ( s ) ;
2024-01-26 10:28:50 +01:00
2023-12-01 10:50:05 +01:00
// Store Emails with server fallback
2022-06-22 05:05:32 +02:00
s . GoToStore ( StoreNavPages . Emails ) ;
2024-01-26 10:28:50 +01:00
Assert . False ( s . Driver . FindElement ( By . Id ( "UseCustomSMTP" ) ) . Selected ) ;
2022-06-22 05:05:32 +02:00
s . Driver . FindElement ( By . Id ( "ConfigureEmailRules" ) ) . Click ( ) ;
2024-01-26 10:28:50 +01:00
Assert . DoesNotContain ( "You need to configure email settings before this feature works" , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
2022-06-22 05:05:32 +02:00
s . GoToStore ( StoreNavPages . Emails ) ;
2024-01-26 10:28:50 +01:00
s . Driver . FindElement ( By . Id ( "UseCustomSMTP" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
2022-01-14 09:50:29 +01:00
CanSetupEmailCore ( s ) ;
2023-01-06 14:18:07 +01:00
2022-06-22 05:05:32 +02:00
// Store Email Rules
s . Driver . FindElement ( By . Id ( "ConfigureEmailRules" ) ) . Click ( ) ;
Assert . Contains ( "There are no rules yet." , s . Driver . PageSource ) ;
Assert . DoesNotContain ( "id=\"SaveEmailRules\"" , s . Driver . PageSource ) ;
Assert . DoesNotContain ( "You need to configure email settings before this feature works" , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
2022-06-22 05:05:32 +02:00
s . Driver . FindElement ( By . Id ( "CreateEmailRule" ) ) . Click ( ) ;
var select = new SelectElement ( s . Driver . FindElement ( By . Id ( "Rules_0__Trigger" ) ) ) ;
2023-12-01 10:50:05 +01:00
select . SelectByText ( "An invoice has been settled" , true ) ;
2022-06-22 05:05:32 +02:00
s . Driver . FindElement ( By . Id ( "Rules_0__To" ) ) . SendKeys ( "test@gmail.com" ) ;
s . Driver . FindElement ( By . Id ( "Rules_0__CustomerEmail" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Rules_0__Subject" ) ) . SendKeys ( "Thanks!" ) ;
2023-04-04 03:52:42 +02:00
s . Driver . FindElement ( By . ClassName ( "note-editable" ) ) . SendKeys ( "Your invoice is settled" ) ;
2022-06-22 05:05:32 +02:00
s . Driver . FindElement ( By . Id ( "SaveEmailRules" ) ) . Click ( ) ;
Assert . Contains ( "Store email rules saved" , s . FindAlertMessage ( ) . Text ) ;
2024-01-26 10:28:50 +01:00
s . GoToStore ( StoreNavPages . Emails ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "UseCustomSMTP" ) ) . Selected ) ;
2020-10-05 09:39:49 +02:00
}
2019-10-06 15:24:28 +02:00
[Fact(Timeout = TestTimeout)]
2019-10-07 09:04:25 +02:00
public async Task CanUseDynamicDns ( )
2019-07-25 12:07:56 +02:00
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( isAdmin : true ) ;
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/services" ) ) ;
Assert . Contains ( "Dynamic DNS" , s . Driver . PageSource ) ;
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/services/dynamic-dns" ) ) ;
s . Driver . AssertNoError ( ) ;
if ( s . Driver . PageSource . Contains ( "pouet.hello.com" ) )
2019-07-25 12:07:56 +02:00
{
2022-01-14 09:50:29 +01:00
// Cleanup old test run
2019-09-11 09:22:41 +02:00
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/services/dynamic-dns/pouet.hello.com/delete" ) ) ;
2021-09-07 04:55:53 +02:00
s . Driver . FindElement ( By . Id ( "ConfirmContinue" ) ) . Click ( ) ;
2019-07-25 12:07:56 +02:00
}
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "AddDynamicDNS" ) ) . Click ( ) ;
s . Driver . AssertNoError ( ) ;
// We will just cheat for test purposes by only querying the server
s . Driver . FindElement ( By . Id ( "ServiceUrl" ) ) . SendKeys ( s . Link ( "/" ) ) ;
s . Driver . FindElement ( By . Id ( "Settings_Hostname" ) ) . SendKeys ( "pouet.hello.com" ) ;
s . Driver . FindElement ( By . Id ( "Settings_Login" ) ) . SendKeys ( "MyLog" ) ;
s . Driver . FindElement ( By . Id ( "Settings_Password" ) ) . SendKeys ( "MyLog" + Keys . Enter ) ;
s . Driver . AssertNoError ( ) ;
Assert . Contains ( "The Dynamic DNS has been successfully queried" , s . Driver . PageSource ) ;
Assert . EndsWith ( "/server/services/dynamic-dns" , s . Driver . Url ) ;
// Try to do the same thing should fail (hostname already exists)
s . Driver . FindElement ( By . Id ( "AddDynamicDNS" ) ) . Click ( ) ;
s . Driver . AssertNoError ( ) ;
s . Driver . FindElement ( By . Id ( "ServiceUrl" ) ) . SendKeys ( s . Link ( "/" ) ) ;
s . Driver . FindElement ( By . Id ( "Settings_Hostname" ) ) . SendKeys ( "pouet.hello.com" ) ;
s . Driver . FindElement ( By . Id ( "Settings_Login" ) ) . SendKeys ( "MyLog" ) ;
s . Driver . FindElement ( By . Id ( "Settings_Password" ) ) . SendKeys ( "MyLog" + Keys . Enter ) ;
s . Driver . AssertNoError ( ) ;
Assert . Contains ( "This hostname already exists" , s . Driver . PageSource ) ;
// Delete it
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/services/dynamic-dns" ) ) ;
Assert . Contains ( "/server/services/dynamic-dns/pouet.hello.com/delete" , s . Driver . PageSource ) ;
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/server/services/dynamic-dns/pouet.hello.com/delete" ) ) ;
s . Driver . FindElement ( By . Id ( "ConfirmContinue" ) ) . Click ( ) ;
s . Driver . AssertNoError ( ) ;
Assert . DoesNotContain ( "/server/services/dynamic-dns/pouet.hello.com/delete" , s . Driver . PageSource ) ;
2019-07-25 12:07:56 +02:00
}
2023-01-06 14:18:07 +01:00
2022-01-11 13:49:56 +01:00
[Fact(Timeout = TestTimeout)]
public async Task CanCreateInvoiceInUI ( )
{
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
s . CreateNewStore ( ) ;
2022-02-17 10:22:09 +01:00
s . GoToInvoices ( ) ;
2024-01-11 16:25:56 +01:00
2022-02-17 10:22:09 +01:00
// Should give us an error message if we try to create an invoice before adding a wallet
2024-01-11 16:25:56 +01:00
s . Driver . FindElement ( By . Id ( "CreateNewInvoice" ) ) . Click ( ) ;
2022-02-17 10:22:09 +01:00
Assert . Contains ( "To create an invoice, you need to" , s . Driver . PageSource ) ;
2024-01-11 16:25:56 +01:00
2022-01-11 13:49:56 +01:00
s . AddDerivationScheme ( ) ;
s . GoToInvoices ( ) ;
s . CreateInvoice ( ) ;
2023-10-11 16:12:45 +02:00
s . Driver . FindElement ( By . CssSelector ( "[data-invoice-state-badge] .dropdown-toggle" ) ) . Click ( ) ;
s . Driver . FindElements ( By . CssSelector ( "[data-invoice-state-badge] .dropdown-menu button" ) ) [ 0 ] . Click ( ) ;
2022-01-11 13:49:56 +01:00
TestUtils . Eventually ( ( ) = > Assert . Contains ( "Invalid (marked)" , s . Driver . PageSource ) ) ;
s . Driver . Navigate ( ) . Refresh ( ) ;
2023-10-11 16:12:45 +02:00
s . Driver . FindElement ( By . CssSelector ( "[data-invoice-state-badge] .dropdown-toggle" ) ) . Click ( ) ;
s . Driver . FindElements ( By . CssSelector ( "[data-invoice-state-badge] .dropdown-menu button" ) ) [ 0 ] . Click ( ) ;
2022-01-11 13:49:56 +01:00
TestUtils . Eventually ( ( ) = > Assert . Contains ( "Settled (marked)" , s . Driver . PageSource ) ) ;
2022-07-07 14:47:59 +02:00
s . Driver . Navigate ( ) . Refresh ( ) ;
2022-01-11 13:49:56 +01:00
2023-10-11 16:12:45 +02:00
s . Driver . FindElement ( By . CssSelector ( "[data-invoice-state-badge] .dropdown-toggle" ) ) . Click ( ) ;
s . Driver . FindElements ( By . CssSelector ( "[data-invoice-state-badge] .dropdown-menu button" ) ) [ 0 ] . Click ( ) ;
2022-01-11 13:49:56 +01:00
TestUtils . Eventually ( ( ) = > Assert . Contains ( "Invalid (marked)" , s . Driver . PageSource ) ) ;
s . Driver . Navigate ( ) . Refresh ( ) ;
2023-10-11 16:12:45 +02:00
s . Driver . FindElement ( By . CssSelector ( "[data-invoice-state-badge] .dropdown-toggle" ) ) . Click ( ) ;
s . Driver . FindElements ( By . CssSelector ( "[data-invoice-state-badge] .dropdown-menu button" ) ) [ 0 ] . Click ( ) ;
2022-01-11 13:49:56 +01:00
TestUtils . Eventually ( ( ) = > Assert . Contains ( "Settled (marked)" , s . Driver . PageSource ) ) ;
}
2023-01-06 14:18:07 +01:00
2022-07-06 14:14:55 +02:00
[Fact(Timeout = TestTimeout)]
public async Task CanUseInvoiceReceipts ( )
{
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
s . CreateNewStore ( ) ;
s . AddDerivationScheme ( ) ;
s . GoToInvoices ( ) ;
var i = s . CreateInvoice ( ) ;
2022-08-26 11:22:00 +02:00
await s . Server . PayTester . InvoiceRepository . MarkInvoiceStatus ( i , InvoiceStatus . Settled ) ;
TestUtils . Eventually ( ( ) = >
{
s . Driver . Navigate ( ) . Refresh ( ) ;
s . Driver . FindElement ( By . Id ( $"Receipt" ) ) . Click ( ) ;
} ) ;
2022-07-06 14:14:55 +02:00
TestUtils . Eventually ( ( ) = >
{
s . Driver . Navigate ( ) . Refresh ( ) ;
Assert . DoesNotContain ( "invoice-unsettled" , s . Driver . PageSource ) ;
Assert . DoesNotContain ( "invoice-processing" , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
} ) ;
2023-03-13 02:12:58 +01:00
Assert . Contains ( "100.00 USD" , s . Driver . PageSource ) ;
2022-07-06 14:14:55 +02:00
Assert . Contains ( i , s . Driver . PageSource ) ;
s . GoToInvoices ( s . StoreId ) ;
i = s . CreateInvoice ( ) ;
s . GoToInvoiceCheckout ( i ) ;
var receipturl = s . Driver . Url + "/receipt" ;
s . Driver . Navigate ( ) . GoToUrl ( receipturl ) ;
s . Driver . FindElement ( By . Id ( "invoice-unsettled" ) ) ;
2023-01-06 14:18:07 +01:00
2022-07-06 14:14:55 +02:00
s . GoToInvoices ( s . StoreId ) ;
s . GoToInvoiceCheckout ( i ) ;
var checkouturi = s . Driver . Url ;
2023-04-05 01:35:50 +02:00
s . PayInvoice ( mine : true ) ;
2022-08-26 11:22:00 +02:00
TestUtils . Eventually ( ( ) = >
{
s . Driver . Navigate ( ) . Refresh ( ) ;
2023-05-11 10:38:40 +02:00
s . Driver . FindElement ( By . Id ( "ReceiptLink" ) ) . Click ( ) ;
2022-08-26 11:22:00 +02:00
} ) ;
2022-07-06 14:14:55 +02:00
TestUtils . Eventually ( ( ) = >
{
s . Driver . Navigate ( ) . Refresh ( ) ;
Assert . DoesNotContain ( "invoice-unsettled" , s . Driver . PageSource ) ;
2023-04-05 01:35:50 +02:00
Assert . Contains ( "\"PaymentDetails\"" , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
} ) ;
2022-07-06 14:14:55 +02:00
s . GoToUrl ( checkouturi ) ;
2022-08-26 11:09:20 +02:00
await s . Server . PayTester . InvoiceRepository . MarkInvoiceStatus ( i , InvoiceStatus . Settled ) ;
2023-01-06 14:18:07 +01:00
2023-05-11 10:38:40 +02:00
TestUtils . Eventually ( ( ) = > s . Driver . FindElement ( By . Id ( "ReceiptLink" ) ) . Click ( ) ) ;
2022-07-06 14:14:55 +02:00
TestUtils . Eventually ( ( ) = >
{
s . Driver . Navigate ( ) . Refresh ( ) ;
Assert . DoesNotContain ( "invoice-unsettled" , s . Driver . PageSource ) ;
Assert . DoesNotContain ( "invoice-processing" , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
} ) ;
2022-07-06 14:14:55 +02:00
}
2019-05-12 10:13:26 +02:00
2022-01-13 09:08:15 +01:00
[Fact(Timeout = TestTimeout)]
public async Task CanSetupStoreViaGuide ( )
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( ) ;
s . GoToUrl ( "/" ) ;
2023-05-10 11:18:29 +02:00
// verify redirected to create store page
Assert . EndsWith ( "/stores/create" , s . Driver . Url ) ;
Assert . Contains ( "Create your first store" , s . Driver . PageSource ) ;
2023-07-19 15:21:16 +02:00
Assert . Contains ( "Create a store to begin accepting payments" , s . Driver . PageSource ) ;
2022-01-14 09:50:29 +01:00
Assert . False ( s . Driver . PageSource . Contains ( "id=\"StoreSelectorDropdown\"" ) , "Store selector dropdown should not be present" ) ;
2022-01-18 02:20:59 +01:00
( _ , string storeId ) = s . CreateNewStore ( ) ;
2023-01-06 14:18:07 +01:00
2023-05-10 11:18:29 +02:00
// should redirect to first store
2022-01-14 09:50:29 +01:00
s . GoToUrl ( "/" ) ;
2022-01-18 02:20:59 +01:00
Assert . Contains ( $"/stores/{storeId}" , s . Driver . Url ) ;
2022-01-14 09:50:29 +01:00
Assert . True ( s . Driver . PageSource . Contains ( "id=\"StoreSelectorDropdown\"" ) , "Store selector dropdown should be present" ) ;
2022-01-18 02:20:59 +01:00
Assert . True ( s . Driver . PageSource . Contains ( "id=\"SetupGuide\"" ) , "Store setup guide should be present" ) ;
2023-05-10 11:18:29 +02:00
s . GoToUrl ( "/stores/create" ) ;
Assert . Contains ( "Create a new store" , s . Driver . PageSource ) ;
Assert . DoesNotContain ( "Create your first store" , s . Driver . PageSource ) ;
Assert . DoesNotContain ( "To start accepting payments, set up a store." , s . Driver . PageSource ) ;
2022-01-13 09:08:15 +01:00
}
2019-10-06 15:24:28 +02:00
[Fact(Timeout = TestTimeout)]
2021-03-31 13:23:36 +02:00
[Trait("Lightning", "Lightning")]
2019-10-07 09:04:25 +02:00
public async Task CanCreateStores ( )
2019-05-12 10:13:26 +02:00
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
s . Server . ActivateLightning ( ) ;
await s . StartAsync ( ) ;
var alice = s . RegisterNewUser ( true ) ;
( string storeName , string storeId ) = s . CreateNewStore ( ) ;
var storeUrl = $"/stores/{storeId}" ;
2022-01-20 12:52:31 +01:00
s . GoToStore ( ) ;
2022-01-14 09:50:29 +01:00
Assert . Contains ( storeName , s . Driver . PageSource ) ;
2022-04-12 09:55:10 +02:00
Assert . DoesNotContain ( "id=\"Dashboard\"" , s . Driver . PageSource ) ;
2022-01-14 09:50:29 +01:00
// verify steps for wallet setup are displayed correctly
s . GoToStore ( StoreNavPages . Dashboard ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "SetupGuide-StoreDone" ) ) . Displayed ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "SetupGuide-Wallet" ) ) . Displayed ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "SetupGuide-Lightning" ) ) . Displayed ) ;
// setup onchain wallet
s . Driver . FindElement ( By . Id ( "SetupGuide-Wallet" ) ) . Click ( ) ;
s . AddDerivationScheme ( ) ;
s . Driver . AssertNoError ( ) ;
2020-06-24 03:34:09 +02:00
2022-01-14 09:50:29 +01:00
s . GoToStore ( StoreNavPages . Dashboard ) ;
2022-04-12 09:55:10 +02:00
Assert . DoesNotContain ( "id=\"SetupGuide\"" , s . Driver . PageSource ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "Dashboard" ) ) . Displayed ) ;
2021-12-31 08:59:02 +01:00
2022-01-14 09:50:29 +01:00
// setup offchain wallet
2022-04-12 09:55:10 +02:00
s . Driver . FindElement ( By . Id ( "StoreNav-LightningBTC" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
s . AddLightningNode ( ) ;
s . Driver . AssertNoError ( ) ;
var successAlert = s . FindAlertMessage ( ) ;
Assert . Contains ( "BTC Lightning node updated." , successAlert . Text ) ;
2021-12-31 08:59:02 +01:00
2022-01-14 09:50:29 +01:00
s . ClickOnAllSectionLinks ( ) ;
s . GoToInvoices ( ) ;
Assert . Contains ( "There are no invoices matching your criteria." , s . Driver . PageSource ) ;
var invoiceId = s . CreateInvoice ( ) ;
s . FindAlertMessage ( ) ;
2023-01-06 14:18:07 +01:00
2022-01-14 09:50:29 +01:00
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 ( ) ;
Assert . Contains ( "Unarchive" , 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 . FindAlertMessage ( ) ;
Assert . DoesNotContain ( "Unarchive" , s . Driver . FindElement ( By . Id ( "btn-archive-toggle" ) ) . Text ) ;
s . GoToInvoices ( ) ;
Assert . Contains ( invoiceId , s . Driver . PageSource ) ;
// archive via list
2023-11-02 08:12:28 +01:00
s . Driver . FindElement ( By . CssSelector ( $".mass-action-select[value=\" { invoiceId } \ "]" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "ArchiveSelected" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
Assert . Contains ( "1 invoice archived" , s . FindAlertMessage ( ) . Text ) ;
Assert . DoesNotContain ( invoiceId , s . Driver . PageSource ) ;
// unarchive via list
2023-05-19 03:42:09 +02:00
s . Driver . FindElement ( By . Id ( "StatusOptionsToggle" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "StatusOptionsIncludeArchived" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
Assert . Contains ( invoiceId , s . Driver . PageSource ) ;
2023-11-02 08:12:28 +01:00
s . Driver . FindElement ( By . CssSelector ( $".mass-action-select[value=\" { invoiceId } \ "]" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "UnarchiveSelected" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
Assert . Contains ( "1 invoice unarchived" , s . FindAlertMessage ( ) . Text ) ;
Assert . Contains ( invoiceId , s . Driver . PageSource ) ;
// When logout out we should not be able to access store and invoice details
s . Logout ( ) ;
s . GoToUrl ( storeUrl ) ;
Assert . Contains ( "ReturnUrl" , s . Driver . Url ) ;
s . Driver . Navigate ( ) . GoToUrl ( invoiceUrl ) ;
Assert . Contains ( "ReturnUrl" , s . Driver . Url ) ;
s . GoToRegister ( ) ;
// When logged in as different user we should not be able to access store and invoice details
var bob = s . RegisterNewUser ( ) ;
s . GoToUrl ( storeUrl ) ;
Assert . Contains ( "ReturnUrl" , s . Driver . Url ) ;
s . Driver . Navigate ( ) . GoToUrl ( invoiceUrl ) ;
s . AssertAccessDenied ( ) ;
s . GoToHome ( ) ;
s . Logout ( ) ;
2024-03-19 14:58:33 +01:00
// Let's add Bob as an employee to alice's store
2022-01-14 09:50:29 +01:00
s . LogIn ( alice ) ;
2024-03-19 14:58:33 +01:00
s . AddUserToStore ( storeId , bob , "Employee" ) ;
2022-01-14 09:50:29 +01:00
s . Logout ( ) ;
// Bob should not have access to store, but should have access to invoice
s . LogIn ( bob ) ;
s . GoToUrl ( storeUrl ) ;
Assert . Contains ( "ReturnUrl" , s . Driver . Url ) ;
s . GoToUrl ( invoiceUrl ) ;
s . Driver . AssertNoError ( ) ;
s . Logout ( ) ;
s . LogIn ( alice ) ;
2022-01-24 12:00:42 +01:00
// Check if we can enable the payment button
s . GoToStore ( StoreNavPages . PayButton ) ;
s . Driver . FindElement ( By . Id ( "enable-pay-button" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "disable-pay-button" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
2022-10-17 12:16:29 +02:00
s . GoToStore ( ) ;
2022-01-24 12:00:42 +01:00
Assert . False ( s . Driver . FindElement ( By . Id ( "AnyoneCanCreateInvoice" ) ) . Selected ) ;
s . Driver . SetCheckbox ( By . Id ( "AnyoneCanCreateInvoice" ) , true ) ;
s . Driver . FindElement ( By . Id ( "Save" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "AnyoneCanCreateInvoice" ) ) . Selected ) ;
2022-10-17 12:16:29 +02:00
// Store settings: Set and unset brand color
s . GoToStore ( ) ;
s . Driver . FindElement ( By . Id ( "BrandColor" ) ) . SendKeys ( "#f7931a" ) ;
s . Driver . FindElement ( By . Id ( "Save" ) ) . Click ( ) ;
Assert . Contains ( "Store successfully updated" , s . FindAlertMessage ( ) . Text ) ;
Assert . Equal ( "#f7931a" , s . Driver . FindElement ( By . Id ( "BrandColor" ) ) . GetAttribute ( "value" ) ) ;
s . Driver . FindElement ( By . Id ( "BrandColor" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Save" ) ) . Click ( ) ;
Assert . Contains ( "Store successfully updated" , s . FindAlertMessage ( ) . Text ) ;
Assert . Equal ( string . Empty , s . Driver . FindElement ( By . Id ( "BrandColor" ) ) . GetAttribute ( "value" ) ) ;
2022-01-24 12:00:42 +01:00
// Alice should be able to delete the store
2022-10-17 12:16:29 +02:00
s . GoToStore ( ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "DeleteStore" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "ConfirmInput" ) ) . SendKeys ( "DELETE" ) ;
s . Driver . FindElement ( By . Id ( "ConfirmContinue" ) ) . Click ( ) ;
s . GoToUrl ( storeUrl ) ;
Assert . Contains ( "ReturnUrl" , s . Driver . Url ) ;
2023-09-11 02:59:17 +02:00
// Archive store
( storeName , storeId ) = s . CreateNewStore ( ) ;
s . Driver . FindElement ( By . Id ( "StoreSelectorToggle" ) ) . Click ( ) ;
Assert . Contains ( storeName , s . Driver . FindElement ( By . Id ( "StoreSelectorMenu" ) ) . Text ) ;
s . Driver . FindElement ( By . Id ( $"StoreSelectorMenuItem-{storeId}" ) ) . Click ( ) ;
s . GoToStore ( ) ;
s . Driver . FindElement ( By . Id ( "btn-archive-toggle" ) ) . Click ( ) ;
Assert . Contains ( "The store has been archived and will no longer appear in the stores list by default." , s . FindAlertMessage ( ) . Text ) ;
s . Driver . FindElement ( By . Id ( "StoreSelectorToggle" ) ) . Click ( ) ;
Assert . DoesNotContain ( storeName , s . Driver . FindElement ( By . Id ( "StoreSelectorMenu" ) ) . Text ) ;
Assert . Contains ( "1 Archived Store" , s . Driver . FindElement ( By . Id ( "StoreSelectorMenu" ) ) . Text ) ;
s . Driver . FindElement ( By . Id ( "StoreSelectorArchived" ) ) . Click ( ) ;
var storeLink = s . Driver . FindElement ( By . Id ( $"Store-{storeId}" ) ) ;
Assert . Contains ( storeName , storeLink . Text ) ;
2024-03-14 10:25:40 +01:00
s . GoToStore ( storeId ) ;
2023-09-11 02:59:17 +02:00
s . Driver . FindElement ( By . Id ( "btn-archive-toggle" ) ) . Click ( ) ;
Assert . Contains ( "The store has been unarchived and will appear in the stores list by default again." , s . FindAlertMessage ( ) . Text ) ;
2019-05-13 10:59:15 +02:00
}
2019-10-12 13:35:30 +02:00
[Fact(Timeout = TestTimeout)]
public async Task CanUsePairing ( )
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/api-access-request" ) ) ;
Assert . Contains ( "ReturnUrl" , s . Driver . Url ) ;
s . GoToRegister ( ) ;
s . RegisterNewUser ( ) ;
s . CreateNewStore ( ) ;
s . AddDerivationScheme ( ) ;
2023-01-06 14:18:07 +01:00
2022-01-19 12:58:02 +01:00
s . GoToStore ( StoreNavPages . Tokens ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "CreateNewToken" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "RequestPairing" ) ) . Click ( ) ;
var pairingCode = AssertUrlHasPairingCode ( s ) ;
2019-10-12 13:35:30 +02:00
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "ApprovePairing" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
Assert . Contains ( pairingCode , s . Driver . PageSource ) ;
2019-10-12 13:35:30 +02:00
2022-01-14 09:50:29 +01:00
var client = new NBitpayClient . Bitpay ( new Key ( ) , s . ServerUri ) ;
await client . AuthorizeClient ( new NBitpayClient . PairingCode ( pairingCode ) ) ;
await client . CreateInvoiceAsync (
new NBitpayClient . Invoice ( ) { Price = 1.000000012 m , Currency = "USD" , FullNotifications = true } ,
NBitpayClient . Facade . Merchant ) ;
2019-10-12 13:35:30 +02:00
2022-01-14 09:50:29 +01:00
client = new NBitpayClient . Bitpay ( new Key ( ) , s . ServerUri ) ;
2019-11-03 08:16:52 +01:00
2022-01-14 09:50:29 +01:00
var code = await client . RequestClientAuthorizationAsync ( "hehe" , NBitpayClient . Facade . Merchant ) ;
s . Driver . Navigate ( ) . GoToUrl ( code . CreateLink ( s . ServerUri ) ) ;
s . Driver . FindElement ( By . Id ( "ApprovePairing" ) ) . Click ( ) ;
await client . CreateInvoiceAsync (
new NBitpayClient . Invoice ( ) { Price = 1.000000012 m , Currency = "USD" , FullNotifications = true } ,
NBitpayClient . Facade . Merchant ) ;
s . Driver . Navigate ( ) . GoToUrl ( s . Link ( "/api-tokens" ) ) ;
s . Driver . FindElement ( By . Id ( "RequestPairing" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "ApprovePairing" ) ) . Click ( ) ;
AssertUrlHasPairingCode ( s ) ;
2019-10-12 13:35:30 +02:00
}
2023-03-20 02:46:46 +01:00
[Fact(Timeout = TestTimeout)]
public async Task CookieReflectProperPermissions ( )
{
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
var alice = s . Server . NewAccount ( ) ;
alice . Register ( false ) ;
await alice . CreateStoreAsync ( ) ;
var bob = s . Server . NewAccount ( ) ;
await bob . CreateStoreAsync ( ) ;
await bob . AddGuest ( alice . UserId ) ;
s . GoToLogin ( ) ;
s . LogIn ( alice . Email , alice . Password ) ;
s . GoToUrl ( $"/cheat/permissions/stores/{bob.StoreId}" ) ;
var pageSource = s . Driver . PageSource ;
AssertPermissions ( pageSource , true ,
new [ ]
{
Policies . CanViewInvoices ,
Policies . CanModifyInvoices ,
Policies . CanViewPaymentRequests ,
2024-03-19 14:58:33 +01:00
Policies . CanViewPullPayments ,
Policies . CanViewPayouts ,
2023-03-20 02:46:46 +01:00
Policies . CanModifyStoreSettingsUnscoped ,
Policies . CanDeleteUser
} ) ;
AssertPermissions ( pageSource , false ,
new [ ]
{
Policies . CanModifyStoreSettings ,
Policies . CanCreateNonApprovedPullPayments ,
Policies . CanCreatePullPayments ,
Policies . CanManagePullPayments ,
Policies . CanModifyServerSettings
} ) ;
s . GoToUrl ( $"/cheat/permissions/stores/{alice.StoreId}" ) ;
pageSource = s . Driver . PageSource ;
AssertPermissions ( pageSource , true ,
new [ ]
{
Policies . CanViewInvoices ,
Policies . CanModifyInvoices ,
Policies . CanViewPaymentRequests ,
Policies . CanViewStoreSettings ,
Policies . CanModifyStoreSettingsUnscoped ,
Policies . CanDeleteUser ,
Policies . CanModifyStoreSettings ,
Policies . CanCreateNonApprovedPullPayments ,
Policies . CanCreatePullPayments ,
2023-09-22 10:24:53 +02:00
Policies . CanManagePullPayments ,
Policies . CanArchivePullPayments ,
2023-03-20 02:46:46 +01:00
} ) ;
AssertPermissions ( pageSource , false ,
new [ ]
{
Policies . CanModifyServerSettings
} ) ;
2023-12-01 09:12:02 +01:00
s . GoToUrl ( "/logout" ) ;
2023-03-20 02:46:46 +01:00
await alice . MakeAdmin ( ) ;
2023-12-01 09:12:02 +01:00
2023-03-20 02:46:46 +01:00
s . GoToLogin ( ) ;
s . LogIn ( alice . Email , alice . Password ) ;
s . GoToUrl ( $"/cheat/permissions/stores/{alice.StoreId}" ) ;
pageSource = s . Driver . PageSource ;
AssertPermissions ( pageSource , true ,
new [ ]
{
Policies . CanViewInvoices ,
Policies . CanModifyInvoices ,
Policies . CanViewPaymentRequests ,
Policies . CanViewStoreSettings ,
Policies . CanModifyStoreSettingsUnscoped ,
Policies . CanDeleteUser ,
Policies . CanModifyStoreSettings ,
Policies . CanCreateNonApprovedPullPayments ,
Policies . CanCreatePullPayments ,
Policies . CanManagePullPayments ,
Policies . CanModifyServerSettings ,
Policies . CanCreateUser ,
Policies . CanManageUsers
} ) ;
}
void AssertPermissions ( string source , bool expected , string [ ] permissions )
{
if ( expected )
{
foreach ( var p in permissions )
Assert . Contains ( p + "<" , source ) ;
}
else
{
foreach ( var p in permissions )
Assert . DoesNotContain ( p + "<" , source ) ;
}
}
2019-10-06 15:24:28 +02:00
[Fact(Timeout = TestTimeout)]
2019-10-07 09:04:25 +02:00
public async Task CanCreateAppPoS ( )
2019-05-12 10:13:26 +02:00
{
2022-02-07 13:18:22 +01:00
using var s = CreateSeleniumTester ( newDb : true ) ;
2022-01-14 09:50:29 +01:00
await s . StartAsync ( ) ;
2022-02-07 13:18:22 +01:00
var userId = s . RegisterNewUser ( true ) ;
2022-01-14 09:50:29 +01:00
s . CreateNewStore ( ) ;
2024-03-19 14:58:33 +01:00
( _ , string appId ) = s . CreateApp ( "PointOfSale" ) ;
2024-03-11 11:04:41 +01:00
s . Driver . FindElement ( By . Id ( "Title" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Title" ) ) . SendKeys ( "Tea shop" ) ;
2023-01-26 01:27:31 +01:00
s . Driver . FindElement ( By . CssSelector ( "label[for='DefaultView_Cart']" ) ) . Click ( ) ;
2023-11-02 19:58:03 +01:00
s . Driver . FindElement ( By . CssSelector ( ".template-item:nth-of-type(1)" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "BuyButtonText" ) ) . SendKeys ( "Take my money" ) ;
2023-06-30 02:13:15 +02:00
s . Driver . FindElement ( By . Id ( "EditorCategories-ts-control" ) ) . SendKeys ( "Drinks" ) ;
2024-03-19 14:59:26 +01:00
s . Driver . ScrollTo ( By . Id ( "CodeTabButton" ) ) ;
s . Driver . FindElement ( By . Id ( "CodeTabButton" ) ) . Click ( ) ;
2023-11-02 19:58:03 +01:00
var template = s . Driver . FindElement ( By . Id ( "TemplateConfig" ) ) . GetAttribute ( "value" ) ;
2023-06-20 15:37:05 +02:00
Assert . Contains ( "\"buyButtonText\": \"Take my money\"" , template ) ;
2023-06-30 02:13:15 +02:00
Assert . Matches ( "\"categories\": \\[\n\\s+\"Drinks\"\n\\s+\\]" , template ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "SaveSettings" ) ) . Click ( ) ;
2022-02-07 10:37:45 +01:00
Assert . Contains ( "App updated" , s . FindAlertMessage ( ) . Text ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "ViewApp" ) ) . Click ( ) ;
2022-02-07 10:37:45 +01:00
var windows = s . Driver . WindowHandles ;
Assert . Equal ( 2 , windows . Count ) ;
s . Driver . SwitchTo ( ) . Window ( windows [ 1 ] ) ;
2022-01-14 09:50:29 +01:00
2022-02-07 10:37:45 +01:00
var posBaseUrl = s . Driver . Url . Replace ( "/cart" , "" ) ;
2022-01-14 09:50:29 +01:00
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" ) ;
Assert . True ( s . Driver . PageSource . Contains ( "Take my money" ) , "PoS not showing correct default view" ) ;
2023-08-09 14:47:28 +02:00
Assert . Equal ( 6 , s . Driver . FindElements ( By . CssSelector ( ".posItem.posItem--displayed" ) ) . Count ) ;
2023-06-30 02:13:15 +02:00
var drinks = s . Driver . FindElement ( By . CssSelector ( "label[for='Category-Drinks']" ) ) ;
Assert . Equal ( "Drinks" , drinks . Text ) ;
drinks . Click ( ) ;
2023-08-09 14:47:28 +02:00
Assert . Single ( s . Driver . FindElements ( By . CssSelector ( ".posItem.posItem--displayed" ) ) ) ;
2023-06-30 02:13:15 +02:00
s . Driver . FindElement ( By . CssSelector ( "label[for='Category-*']" ) ) . Click ( ) ;
2023-08-09 14:47:28 +02:00
Assert . Equal ( 6 , s . Driver . FindElements ( By . CssSelector ( ".posItem.posItem--displayed" ) ) . Count ) ;
2022-01-14 09:50:29 +01:00
s . Driver . Url = posBaseUrl + "/static" ;
Assert . False ( s . Driver . PageSource . Contains ( "Cart" ) , "Static PoS not showing correct view" ) ;
s . Driver . Url = posBaseUrl + "/cart" ;
Assert . True ( s . Driver . PageSource . Contains ( "Cart" ) , "Cart PoS not showing correct view" ) ;
2022-02-07 13:18:22 +01:00
// Let's set change the root app
s . GoToHome ( ) ;
s . GoToServer ( ServerNavPages . Policies ) ;
s . Driver . ScrollTo ( By . Id ( "RootAppId" ) ) ;
var select = new SelectElement ( s . Driver . FindElement ( By . Id ( "RootAppId" ) ) ) ;
select . SelectByText ( "Point of" , true ) ;
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
2023-11-21 12:00:31 +01:00
// Make sure after login, we are not redirected to the PoS
2022-02-07 13:18:22 +01:00
s . Logout ( ) ;
s . LogIn ( userId ) ;
Assert . DoesNotContain ( "Tea shop" , s . Driver . PageSource ) ;
2022-04-25 10:37:46 +02:00
var prevUrl = s . Driver . Url ;
2022-04-11 10:49:28 +02:00
// We are only if explicitly going to /
2022-02-07 13:18:22 +01:00
s . GoToUrl ( "/" ) ;
Assert . Contains ( "Tea shop" , s . Driver . PageSource ) ;
2023-11-21 12:00:31 +01:00
// Check redirect to canonical url
s . GoToUrl ( posBaseUrl ) ;
Assert . Equal ( "/" , new Uri ( s . Driver . Url , UriKind . Absolute ) . AbsolutePath ) ;
2022-02-07 13:18:22 +01:00
// Let's check with domain mapping as well.
2023-11-21 12:00:31 +01:00
s . Driver . Navigate ( ) . GoToUrl ( new Uri ( prevUrl , UriKind . Absolute ) ) ;
2022-02-07 13:18:22 +01:00
s . GoToServer ( ServerNavPages . Policies ) ;
s . Driver . ScrollTo ( By . Id ( "RootAppId" ) ) ;
select = new SelectElement ( s . Driver . FindElement ( By . Id ( "RootAppId" ) ) ) ;
select . SelectByText ( "None" , true ) ;
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
s . Driver . ScrollTo ( By . Id ( "RootAppId" ) ) ;
s . Driver . FindElement ( By . Id ( "AddDomainButton" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "DomainToAppMapping_0__Domain" ) ) . SendKeys ( new Uri ( s . Driver . Url , UriKind . Absolute ) . DnsSafeHost ) ;
select = new SelectElement ( s . Driver . FindElement ( By . Id ( "DomainToAppMapping_0__AppId" ) ) ) ;
select . SelectByText ( "Point of" , true ) ;
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
2023-02-02 12:53:42 +01:00
Assert . Contains ( "Policies updated successfully" , s . FindAlertMessage ( ) . Text ) ;
2023-11-21 12:00:31 +01:00
// Make sure after login, we are not redirected to the PoS
2022-02-07 13:18:22 +01:00
s . Logout ( ) ;
s . LogIn ( userId ) ;
Assert . DoesNotContain ( "Tea shop" , s . Driver . PageSource ) ;
2022-04-11 10:49:28 +02:00
// We are only if explicitly going to /
2022-02-07 13:18:22 +01:00
s . GoToUrl ( "/" ) ;
Assert . Contains ( "Tea shop" , s . Driver . PageSource ) ;
2023-11-21 12:00:31 +01:00
// Check redirect to canonical url
s . GoToUrl ( posBaseUrl ) ;
Assert . Equal ( "/" , new Uri ( s . Driver . Url , UriKind . Absolute ) . AbsolutePath ) ;
2023-09-11 02:59:17 +02:00
// Archive
s . Driver . SwitchTo ( ) . Window ( windows [ 0 ] ) ;
2023-11-21 12:00:31 +01:00
Assert . True ( s . Driver . ElementDoesNotExist ( By . Id ( "Nav-ArchivedApps" ) ) ) ;
2023-09-11 02:59:17 +02:00
s . Driver . FindElement ( By . Id ( "btn-archive-toggle" ) ) . Click ( ) ;
Assert . Contains ( "The app has been archived and will no longer appear in the apps list by default." , s . FindAlertMessage ( ) . Text ) ;
Assert . True ( s . Driver . ElementDoesNotExist ( By . Id ( "ViewApp" ) ) ) ;
Assert . Contains ( "1 Archived App" , s . Driver . FindElement ( By . Id ( "Nav-ArchivedApps" ) ) . Text ) ;
s . Driver . Navigate ( ) . GoToUrl ( posBaseUrl ) ;
Assert . Contains ( "Page not found" , s . Driver . Title , StringComparison . OrdinalIgnoreCase ) ;
s . Driver . Navigate ( ) . Back ( ) ;
s . Driver . FindElement ( By . Id ( "Nav-ArchivedApps" ) ) . Click ( ) ;
// Unarchive
s . Driver . FindElement ( By . Id ( $"App-{appId}" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "btn-archive-toggle" ) ) . Click ( ) ;
Assert . Contains ( "The app has been unarchived and will appear in the apps list by default again." , s . FindAlertMessage ( ) . Text ) ;
2019-05-12 10:13:26 +02:00
}
2019-10-06 15:24:28 +02:00
[Fact(Timeout = TestTimeout)]
2021-01-25 14:10:19 +01:00
public async Task CanCreateCrowdfundingApp ( )
2019-05-12 10:13:26 +02:00
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( ) ;
s . CreateNewStore ( ) ;
s . AddDerivationScheme ( ) ;
2024-03-19 14:58:33 +01:00
( _ , string appId ) = s . CreateApp ( "Crowdfund" ) ;
2024-03-11 11:04:41 +01:00
s . Driver . FindElement ( By . Id ( "Title" ) ) . Clear ( ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "Title" ) ) . SendKeys ( "Kukkstarter" ) ;
s . Driver . FindElement ( By . CssSelector ( "div.note-editable.card-block" ) ) . SendKeys ( "1BTC = 1BTC" ) ;
2022-02-07 10:37:45 +01:00
s . Driver . FindElement ( By . Id ( "TargetCurrency" ) ) . Clear ( ) ;
2023-10-06 15:58:02 +02:00
s . Driver . FindElement ( By . Id ( "TargetCurrency" ) ) . SendKeys ( "EUR" ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "TargetAmount" ) ) . SendKeys ( "700" ) ;
2023-01-06 14:18:07 +01:00
2022-09-09 12:26:11 +02:00
// test wrong dates
s . Driver . ExecuteJavaScript ( "const now = new Date();document.getElementById('StartDate').value = now.toISOString();" +
"const yst = new Date(now.setDate(now.getDate() -1));document.getElementById('EndDate').value = yst.toISOString()" ) ;
s . Driver . FindElement ( By . Id ( "SaveSettings" ) ) . Click ( ) ;
Assert . Contains ( "End date cannot be before start date" , s . Driver . PageSource ) ;
Assert . DoesNotContain ( "App updated" , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
2022-09-09 12:26:11 +02:00
// unset end date
s . Driver . ExecuteJavaScript ( "document.getElementById('EndDate').value = ''" ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "SaveSettings" ) ) . Click ( ) ;
2022-02-07 10:37:45 +01:00
Assert . Contains ( "App updated" , s . FindAlertMessage ( ) . Text ) ;
2024-02-21 14:41:21 +01:00
var editUrl = s . Driver . Url ;
2023-10-06 15:58:02 +02:00
2024-02-21 14:41:21 +01:00
// Check public page
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "ViewApp" ) ) . Click ( ) ;
2022-02-07 10:37:45 +01:00
var windows = s . Driver . WindowHandles ;
Assert . Equal ( 2 , windows . Count ) ;
s . Driver . SwitchTo ( ) . Window ( windows [ 1 ] ) ;
2023-09-11 02:59:17 +02:00
var cfUrl = s . Driver . Url ;
2022-02-07 10:37:45 +01:00
2024-02-21 14:41:21 +01:00
Assert . Equal ( "Currently active!" , s . Driver . FindElement ( By . CssSelector ( "[data-test='time-state']" ) ) . Text ) ;
2023-01-06 14:18:07 +01:00
2023-10-06 15:58:02 +02:00
// Contribute
s . Driver . FindElement ( By . Id ( "crowdfund-body-header-cta" ) ) . Click ( ) ;
TestUtils . Eventually ( ( ) = >
{
2024-02-21 14:41:21 +01:00
s . Driver . WaitUntilAvailable ( By . Name ( "btcpay" ) ) ;
var frameElement = s . Driver . FindElement ( By . Name ( "btcpay" ) ) ;
Assert . True ( frameElement . Displayed ) ;
var iframe = s . Driver . SwitchTo ( ) . Frame ( frameElement ) ;
iframe . WaitUntilAvailable ( By . Id ( "Checkout-v2" ) ) ;
var closeButton = iframe . FindElement ( By . Id ( "close" ) ) ;
Assert . True ( closeButton . Displayed ) ;
closeButton . Click ( ) ;
2023-10-06 15:58:02 +02:00
} ) ;
s . Driver . AssertElementNotFound ( By . Name ( "btcpay" ) ) ;
// Back to admin view
2022-02-07 10:37:45 +01:00
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( windows [ 0 ] ) ;
2023-09-11 02:59:17 +02:00
// Archive
Assert . True ( s . Driver . ElementDoesNotExist ( By . Id ( "Nav-ArchivedApps" ) ) ) ;
s . Driver . SwitchTo ( ) . Window ( windows [ 0 ] ) ;
s . Driver . FindElement ( By . Id ( "btn-archive-toggle" ) ) . Click ( ) ;
Assert . Contains ( "The app has been archived and will no longer appear in the apps list by default." , s . FindAlertMessage ( ) . Text ) ;
Assert . True ( s . Driver . ElementDoesNotExist ( By . Id ( "ViewApp" ) ) ) ;
Assert . Contains ( "1 Archived App" , s . Driver . FindElement ( By . Id ( "Nav-ArchivedApps" ) ) . Text ) ;
s . Driver . Navigate ( ) . GoToUrl ( cfUrl ) ;
Assert . Contains ( "Page not found" , s . Driver . Title , StringComparison . OrdinalIgnoreCase ) ;
s . Driver . Navigate ( ) . Back ( ) ;
s . Driver . FindElement ( By . Id ( "Nav-ArchivedApps" ) ) . Click ( ) ;
// Unarchive
s . Driver . FindElement ( By . Id ( $"App-{appId}" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "btn-archive-toggle" ) ) . Click ( ) ;
Assert . Contains ( "The app has been unarchived and will appear in the apps list by default again." , s . FindAlertMessage ( ) . Text ) ;
2024-02-21 14:41:21 +01:00
// Crowdfund with form
s . GoToUrl ( editUrl ) ;
new SelectElement ( s . Driver . FindElement ( By . Id ( "FormId" ) ) ) . SelectByValue ( "Email" ) ;
s . Driver . FindElement ( By . Id ( "SaveSettings" ) ) . Click ( ) ;
Assert . Contains ( "App updated" , s . FindAlertMessage ( ) . Text ) ;
s . Driver . FindElement ( By . Id ( "ViewApp" ) ) . Click ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
s . Driver . FindElement ( By . Id ( "crowdfund-body-header-cta" ) ) . Click ( ) ;
Assert . Contains ( "Enter your email" , s . Driver . PageSource ) ;
s . Driver . FindElement ( By . Name ( "buyerEmail" ) ) . SendKeys ( "test-without-perk@crowdfund.com" ) ;
s . Driver . FindElement ( By . CssSelector ( "input[type='submit']" ) ) . Click ( ) ;
s . PayInvoice ( true , 10 ) ;
var invoiceId = s . Driver . Url [ ( s . Driver . Url . LastIndexOf ( "/" , StringComparison . Ordinal ) + 1 ) . . ] ;
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
s . GoToInvoice ( invoiceId ) ;
Assert . Contains ( "test-without-perk@crowdfund.com" , s . Driver . PageSource ) ;
// Crowdfund with perk
s . GoToUrl ( editUrl ) ;
s . Driver . ScrollTo ( By . Id ( "btAddItem" ) ) ;
s . Driver . FindElement ( By . Id ( "btAddItem" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "EditorTitle" ) ) . SendKeys ( "Perk 1" ) ;
s . Driver . FindElement ( By . Id ( "EditorAmount" ) ) . SendKeys ( "20" ) ;
2024-03-19 14:59:26 +01:00
// Test autogenerated ID
Assert . Equal ( "perk-1" , s . Driver . FindElement ( By . Id ( "EditorId" ) ) . GetAttribute ( "value" ) ) ;
s . Driver . FindElement ( By . Id ( "EditorId" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "EditorId" ) ) . SendKeys ( "Perk-1" ) ;
s . Driver . ScrollTo ( By . Id ( "CodeTabButton" ) ) ;
s . Driver . FindElement ( By . Id ( "CodeTabButton" ) ) . Click ( ) ;
var template = s . Driver . FindElement ( By . Id ( "TemplateConfig" ) ) . GetAttribute ( "value" ) ;
Assert . Contains ( "\"title\": \"Perk 1\"" , template ) ;
Assert . Contains ( "\"id\": \"Perk-1\"" , template ) ;
2024-02-21 14:41:21 +01:00
s . Driver . FindElement ( By . Id ( "SaveSettings" ) ) . Click ( ) ;
Assert . Contains ( "App updated" , s . FindAlertMessage ( ) . Text ) ;
s . Driver . FindElement ( By . Id ( "ViewApp" ) ) . Click ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
s . Driver . WaitForElement ( By . Id ( "Perk-1" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . CssSelector ( "#Perk-1 button[type=\"submit\"]" ) ) . Submit ( ) ;
Assert . Contains ( "Enter your email" , s . Driver . PageSource ) ;
s . Driver . FindElement ( By . Name ( "buyerEmail" ) ) . SendKeys ( "test-with-perk@crowdfund.com" ) ;
s . Driver . FindElement ( By . CssSelector ( "input[type='submit']" ) ) . Click ( ) ;
s . PayInvoice ( true , 20 ) ;
invoiceId = s . Driver . Url [ ( s . Driver . Url . LastIndexOf ( "/" , StringComparison . Ordinal ) + 1 ) . . ] ;
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
s . GoToInvoice ( invoiceId ) ;
Assert . Contains ( "test-with-perk@crowdfund.com" , s . Driver . PageSource ) ;
2019-05-12 10:13:26 +02:00
}
2019-10-06 15:24:28 +02:00
[Fact(Timeout = TestTimeout)]
2019-10-07 09:04:25 +02:00
public async Task CanCreatePayRequest ( )
2019-05-12 10:13:26 +02:00
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( ) ;
s . CreateNewStore ( ) ;
2024-01-11 16:25:56 +01:00
s . Driver . FindElement ( By . Id ( "StoreNav-PaymentRequests" ) ) . Click ( ) ;
// Should give us an error message if we try to create a payment request before adding a wallet
s . Driver . FindElement ( By . Id ( "CreatePaymentRequest" ) ) . Click ( ) ;
Assert . Contains ( "To create a payment request, you need to" , s . Driver . PageSource ) ;
2022-01-14 09:50:29 +01:00
2024-01-11 16:25:56 +01:00
s . AddDerivationScheme ( ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "StoreNav-PaymentRequests" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "CreatePaymentRequest" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Title" ) ) . SendKeys ( "Pay123" ) ;
2023-09-19 03:10:13 +02:00
s . Driver . FindElement ( By . Id ( "Amount" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( ".01" ) ;
2022-04-11 10:50:30 +02:00
var currencyInput = s . Driver . FindElement ( By . Id ( "Currency" ) ) ;
Assert . Equal ( "USD" , currencyInput . GetAttribute ( "value" ) ) ;
currencyInput . Clear ( ) ;
currencyInput . SendKeys ( "BTC" ) ;
2023-01-06 14:18:07 +01:00
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
2022-06-20 04:52:12 +02:00
s . Driver . FindElement ( By . XPath ( "//a[starts-with(@id, 'Edit-')]" ) ) . Click ( ) ;
var editUrl = s . Driver . Url ;
2023-01-06 14:18:07 +01:00
2022-02-09 15:37:15 +01:00
s . Driver . FindElement ( By . Id ( "ViewPaymentRequest" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2022-06-20 04:52:12 +02:00
var viewUrl = s . Driver . Url ;
2023-11-20 02:45:43 +01:00
Assert . Equal ( "Pay Invoice" , s . Driver . FindElement ( By . Id ( "PayInvoice" ) ) . Text . Trim ( ) ) ;
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2023-01-06 14:18:07 +01:00
2022-01-14 09:50:29 +01:00
// expire
s . Driver . ExecuteJavaScript ( "document.getElementById('ExpiryDate').value = '2021-01-21T21:00:00.000Z'" ) ;
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
2022-06-20 04:52:12 +02:00
s . Driver . FindElement ( By . XPath ( "//a[starts-with(@id, 'Edit-')]" ) ) . Click ( ) ;
2023-01-06 14:18:07 +01:00
2022-06-20 04:52:12 +02:00
s . GoToUrl ( viewUrl ) ;
2022-01-14 09:50:29 +01:00
Assert . Equal ( "Expired" , s . Driver . WaitForElement ( By . CssSelector ( "[data-test='status']" ) ) . Text ) ;
// unexpire
2022-06-20 04:52:12 +02:00
s . GoToUrl ( editUrl ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "ClearExpiryDate" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
2022-06-20 04:52:12 +02:00
s . Driver . FindElement ( By . XPath ( "//a[starts-with(@id, 'Edit-')]" ) ) . Click ( ) ;
2022-12-14 06:01:48 +01:00
// amount and currency should be editable, because no invoice exists
s . GoToUrl ( editUrl ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "Amount" ) ) . Enabled ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "Currency" ) ) . Enabled ) ;
2023-01-06 14:18:07 +01:00
2022-06-20 04:52:12 +02:00
s . GoToUrl ( viewUrl ) ;
2023-11-20 02:45:43 +01:00
Assert . Equal ( "Pay Invoice" , s . Driver . FindElement ( By . Id ( "PayInvoice" ) ) . Text . Trim ( ) ) ;
2023-01-06 14:18:07 +01:00
2023-11-20 02:45:43 +01:00
// test invoice creation
s . Driver . FindElement ( By . Id ( "PayInvoice" ) ) . Click ( ) ;
TestUtils . Eventually ( ( ) = >
{
s . Driver . WaitUntilAvailable ( By . Name ( "btcpay" ) ) ;
var frameElement = s . Driver . FindElement ( By . Name ( "btcpay" ) ) ;
Assert . True ( frameElement . Displayed ) ;
var iframe = s . Driver . SwitchTo ( ) . Frame ( frameElement ) ;
iframe . WaitUntilAvailable ( By . Id ( "Checkout-v2" ) ) ;
IWebElement closebutton = null ;
TestUtils . Eventually ( ( ) = >
{
closebutton = iframe . FindElement ( By . Id ( "close" ) ) ;
Assert . True ( closebutton . Displayed ) ;
} ) ;
closebutton . Click ( ) ;
s . Driver . AssertElementNotFound ( By . Name ( "btcpay" ) ) ;
} ) ;
2023-01-06 14:18:07 +01:00
2022-12-14 06:01:48 +01:00
// amount and currency should not be editable, because invoice exists
2022-06-20 04:52:12 +02:00
s . GoToUrl ( editUrl ) ;
2022-12-14 06:01:48 +01:00
Assert . False ( s . Driver . FindElement ( By . Id ( "Amount" ) ) . Enabled ) ;
Assert . False ( s . Driver . FindElement ( By . Id ( "Currency" ) ) . Enabled ) ;
2023-01-06 14:18:07 +01:00
2022-12-14 06:01:48 +01:00
// archive (from details page)
2022-02-09 15:37:15 +01:00
var payReqId = s . Driver . Url . Split ( '/' ) . Last ( ) ;
s . Driver . FindElement ( By . Id ( "ArchivePaymentRequest" ) ) . Click ( ) ;
Assert . Contains ( "The payment request has been archived" , s . FindAlertMessage ( ) . Text ) ;
Assert . DoesNotContain ( "Pay123" , s . Driver . PageSource ) ;
2023-07-06 10:02:23 +02:00
s . Driver . FindElement ( By . Id ( "StatusOptionsToggle" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "StatusOptionsIncludeArchived" ) ) . Click ( ) ;
2022-02-09 15:37:15 +01:00
Assert . Contains ( "Pay123" , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
2022-02-09 15:37:15 +01:00
// unarchive (from list)
2023-07-06 10:02:23 +02:00
s . Driver . FindElement ( By . Id ( $"ToggleActions-{payReqId}" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( $"ToggleArchival-{payReqId}" ) ) . Click ( ) ;
2022-02-09 15:37:15 +01:00
Assert . Contains ( "The payment request has been unarchived" , s . FindAlertMessage ( ) . Text ) ;
Assert . Contains ( "Pay123" , s . Driver . PageSource ) ;
2023-09-19 03:10:13 +02:00
// payment
s . GoToUrl ( viewUrl ) ;
2023-11-20 02:45:43 +01:00
s . Driver . FindElement ( By . Id ( "PayInvoice" ) ) . Click ( ) ;
2023-09-19 03:10:13 +02:00
TestUtils . Eventually ( ( ) = >
{
2023-11-20 02:45:43 +01:00
s . Driver . WaitUntilAvailable ( By . Name ( "btcpay" ) ) ;
var frameElement = s . Driver . FindElement ( By . Name ( "btcpay" ) ) ;
Assert . True ( frameElement . Displayed ) ;
var iframe = s . Driver . SwitchTo ( ) . Frame ( frameElement ) ;
iframe . WaitUntilAvailable ( By . Id ( "Checkout-v2" ) ) ;
// Pay full amount
s . PayInvoice ( ) ;
// Processing
TestUtils . Eventually ( ( ) = >
{
var processingSection = s . Driver . WaitForElement ( By . Id ( "processing" ) ) ;
Assert . True ( processingSection . Displayed ) ;
Assert . Contains ( "Payment Received" , processingSection . Text ) ;
Assert . Contains ( "Your payment has been received and is now processing" , processingSection . Text ) ;
} ) ;
2023-09-19 03:10:13 +02:00
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles [ 0 ] ) ;
Assert . Equal ( "Processing" , s . Driver . WaitForElement ( By . CssSelector ( "[data-test='status']" ) ) . Text ) ;
s . Driver . SwitchTo ( ) . Frame ( frameElement ) ;
2023-09-19 03:10:13 +02:00
2023-11-20 02:45:43 +01:00
// Mine
s . MineBlockOnInvoiceCheckout ( ) ;
TestUtils . Eventually ( ( ) = >
{
Assert . Contains ( "Mined 1 block" ,
s . Driver . WaitForElement ( By . Id ( "CheatSuccessMessage" ) ) . Text ) ;
} ) ;
s . Driver . FindElement ( By . Id ( "close" ) ) . Click ( ) ;
s . Driver . AssertElementNotFound ( By . Name ( "btcpay" ) ) ;
2023-09-19 03:10:13 +02:00
} ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles [ 0 ] ) ;
2023-09-19 03:10:13 +02:00
Assert . Equal ( "Settled" , s . Driver . WaitForElement ( By . CssSelector ( "[data-test='status']" ) ) . Text ) ;
2019-05-12 10:13:26 +02:00
}
2020-03-23 07:46:54 +01:00
[Fact(Timeout = TestTimeout)]
public async Task CanUseCoinSelection ( )
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
( _ , string storeId ) = s . CreateNewStore ( ) ;
s . GenerateWallet ( "BTC" , "" , false , true ) ;
var walletId = new WalletId ( storeId , "BTC" ) ;
s . GoToWallet ( walletId , WalletsNavPages . Receive ) ;
s . Driver . FindElement ( By . Id ( "generateButton" ) ) . Click ( ) ;
2023-09-19 02:56:11 +02:00
var addressStr = s . Driver . FindElement ( By . Id ( "Address" ) ) . GetAttribute ( "data-text" ) ;
2022-01-14 09:50:29 +01:00
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 + + )
2020-03-23 07:46:54 +01:00
{
2022-01-14 09:50:29 +01:00
await s . Server . ExplorerNode . SendToAddressAsync ( address , Money . Coins ( 1.0 m ) ) ;
2020-03-23 07:46:54 +01:00
}
2022-01-14 09:50:29 +01:00
var targetTx = await s . Server . ExplorerNode . SendToAddressAsync ( address , Money . Coins ( 1.2 m ) ) ;
var tx = await s . Server . ExplorerNode . GetRawTransactionAsync ( targetTx ) ;
var spentOutpoint = new OutPoint ( targetTx ,
tx . Outputs . FindIndex ( txout = > txout . Value = = Money . Coins ( 1.2 m ) ) ) ;
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 ) ;
var wallet = s . Server . PayTester . GetService < BTCPayWalletProvider > ( ) . GetWallet ( walletId . CryptoCode ) ;
wallet . InvalidateCache ( x . AccountDerivation ) ;
Assert . Contains (
await wallet . GetUnspentCoins ( x . AccountDerivation ) ,
coin = > coin . OutPoint = = spentOutpoint ) ;
} ) ;
await s . Server . ExplorerNode . GenerateAsync ( 1 ) ;
s . GoToWallet ( walletId ) ;
s . Driver . WaitForAndClick ( By . Id ( "toggleInputSelection" ) ) ;
s . Driver . WaitForElement ( By . Id ( spentOutpoint . ToString ( ) ) ) ;
Assert . Equal ( "true" ,
s . Driver . FindElement ( By . Name ( "InputSelection" ) ) . GetAttribute ( "value" ) . ToLowerInvariant ( ) ) ;
2022-07-22 13:29:53 +02:00
s . Driver . FindElement ( By . Id ( spentOutpoint . ToString ( ) ) ) ;
2022-01-14 09:50:29 +01:00
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.3 m ) ;
s . Driver . FindElement ( By . Id ( "SignTransaction" ) ) . Click ( ) ;
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 ;
tx = await s . Server . ExplorerNode . GetRawTransactionAsync ( new uint256 ( txid ) ) ;
Assert . Single ( tx . Inputs ) ;
Assert . Equal ( spentOutpoint , tx . Inputs [ 0 ] . PrevOut ) ;
2020-03-23 07:46:54 +01:00
}
2020-11-06 12:42:26 +01:00
[Fact(Timeout = TestTimeout)]
public async Task CanUseWebhooks ( )
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
s . CreateNewStore ( ) ;
s . GoToStore ( StoreNavPages . Webhooks ) ;
2020-11-06 12:42:26 +01:00
2022-01-14 09:50:29 +01:00
TestLogs . LogInformation ( "Let's create two webhooks" ) ;
for ( var i = 0 ; i < 2 ; i + + )
{
s . Driver . FindElement ( By . Id ( "CreateWebhook" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Name ( "PayloadUrl" ) ) . SendKeys ( $"http://127.0.0.1/callback{i}" ) ;
new SelectElement ( s . Driver . FindElement ( By . Id ( "Everything" ) ) ) . SelectByValue ( "false" ) ;
s . Driver . FindElement ( By . Id ( "InvoiceCreated" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "InvoiceProcessing" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Name ( "add" ) ) . Click ( ) ;
}
2020-11-06 12:42:26 +01:00
2022-01-14 09:50:29 +01:00
TestLogs . LogInformation ( "Let's delete one of them" ) ;
var deletes = s . Driver . FindElements ( By . LinkText ( "Delete" ) ) ;
Assert . Equal ( 2 , deletes . Count ) ;
deletes [ 0 ] . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "ConfirmInput" ) ) . SendKeys ( "DELETE" ) ;
s . Driver . FindElement ( By . Id ( "ConfirmContinue" ) ) . Click ( ) ;
deletes = s . Driver . FindElements ( By . LinkText ( "Delete" ) ) ;
Assert . Single ( deletes ) ;
s . FindAlertMessage ( ) ;
TestLogs . LogInformation ( "Let's try to update one of them" ) ;
s . Driver . FindElement ( By . LinkText ( "Modify" ) ) . Click ( ) ;
2023-11-02 08:12:28 +01:00
using var server = new FakeServer ( ) ;
2022-01-14 09:50:29 +01:00
await server . Start ( ) ;
s . Driver . FindElement ( By . Name ( "PayloadUrl" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Name ( "PayloadUrl" ) ) . SendKeys ( server . ServerUri . AbsoluteUri ) ;
s . Driver . FindElement ( By . Name ( "Secret" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Name ( "Secret" ) ) . SendKeys ( "HelloWorld" ) ;
s . Driver . FindElement ( By . Name ( "update" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
s . Driver . FindElement ( By . LinkText ( "Modify" ) ) . Click ( ) ;
2021-01-22 17:49:26 +01:00
2022-01-14 09:50:29 +01:00
// This one should be checked
2022-11-02 16:55:05 +01:00
Assert . Contains ( "value=\"InvoiceProcessing\" checked" , s . Driver . PageSource ) ;
Assert . Contains ( "value=\"InvoiceCreated\" checked" , s . Driver . PageSource ) ;
2022-01-14 09:50:29 +01:00
// This one never been checked
2022-11-02 16:55:05 +01:00
Assert . DoesNotContain ( "value=\"InvoiceReceivedPayment\" checked" , s . Driver . PageSource ) ;
2020-11-06 12:42:26 +01:00
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Name ( "update" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
Assert . Contains ( server . ServerUri . AbsoluteUri , s . Driver . PageSource ) ;
2020-11-06 12:42:26 +01:00
2022-01-14 09:50:29 +01:00
TestLogs . LogInformation ( "Let's see if we can generate an event" ) ;
2022-01-20 12:52:31 +01:00
s . GoToStore ( ) ;
2022-01-14 09:50:29 +01:00
s . AddDerivationScheme ( ) ;
s . CreateInvoice ( ) ;
var request = await server . GetNextRequest ( ) ;
var headers = request . Request . Headers ;
var actualSig = headers [ "BTCPay-Sig" ] . First ( ) ;
var bytes = await request . Request . Body . ReadBytesAsync ( ( int ) headers . ContentLength . Value ) ;
var expectedSig =
2022-07-08 09:30:52 +02:00
$"sha256={Encoders.Hex.EncodeData(NBitcoin.Crypto.Hashes.HMACSHA256(Encoding.UTF8.GetBytes(" HelloWorld "), bytes))}" ;
2022-01-14 09:50:29 +01:00
Assert . Equal ( expectedSig , actualSig ) ;
request . Response . StatusCode = 200 ;
server . Done ( ) ;
TestLogs . LogInformation ( "Let's make a failed event" ) ;
2023-11-02 08:12:28 +01:00
var invoiceId = s . CreateInvoice ( ) ;
2022-01-14 09:50:29 +01:00
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 ( StoreNavPages . Webhooks ) ;
s . Driver . FindElement ( By . LinkText ( "Modify" ) ) . Click ( ) ;
var elements = s . Driver . FindElements ( By . ClassName ( "redeliver" ) ) ;
2023-01-06 14:18:07 +01:00
2022-01-14 09:50:29 +01:00
// One worked, one failed
s . Driver . FindElement ( By . ClassName ( "fa-times" ) ) ;
s . Driver . FindElement ( By . ClassName ( "fa-check" ) ) ;
elements [ 0 ] . Click ( ) ;
s . FindAlertMessage ( ) ;
request = await server . GetNextRequest ( ) ;
request . Response . StatusCode = 404 ;
server . Done ( ) ;
TestLogs . LogInformation ( "Can we browse the json content?" ) ;
CanBrowseContent ( s ) ;
2021-01-22 17:49:26 +01:00
2022-01-14 09:50:29 +01:00
s . GoToInvoices ( ) ;
2023-11-02 08:12:28 +01:00
s . Driver . FindElement ( By . LinkText ( invoiceId ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
CanBrowseContent ( s ) ;
var element = s . Driver . FindElement ( By . ClassName ( "redeliver" ) ) ;
element . Click ( ) ;
s . FindAlertMessage ( ) ;
request = await server . GetNextRequest ( ) ;
request . Response . StatusCode = 404 ;
server . Done ( ) ;
TestLogs . LogInformation ( "Let's see if we can delete store with some webhooks inside" ) ;
2022-05-12 10:59:50 +02:00
s . GoToStore ( ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "DeleteStore" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "ConfirmInput" ) ) . SendKeys ( "DELETE" ) ;
s . Driver . FindElement ( By . Id ( "ConfirmContinue" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
2020-11-06 12:42:26 +01:00
}
2019-10-06 15:24:28 +02:00
[Fact(Timeout = TestTimeout)]
2021-10-12 11:37:13 +02:00
public async Task CanImportMnemonic ( )
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
foreach ( var isHotwallet in new [ ] { false , true } )
2021-10-12 11:37:13 +02:00
{
2022-01-14 09:50:29 +01:00
var cryptoCode = "BTC" ;
2022-01-19 12:58:02 +01:00
s . CreateNewStore ( ) ;
2022-02-10 04:24:28 +01:00
s . GenerateWallet ( cryptoCode , "melody lizard phrase voice unique car opinion merge degree evil swift cargo" , isHotWallet : isHotwallet ) ;
2022-01-19 12:58:02 +01:00
s . GoToWalletSettings ( cryptoCode ) ;
2022-01-14 09:50:29 +01:00
if ( isHotwallet )
Assert . Contains ( "View seed" , s . Driver . PageSource ) ;
else
Assert . DoesNotContain ( "View seed" , s . Driver . PageSource ) ;
2021-10-12 11:37:13 +02:00
}
}
[Fact(Timeout = TestTimeout)]
2019-10-07 09:04:25 +02:00
public async Task CanManageWallet ( )
2019-05-13 10:59:15 +02:00
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
( _ , string storeId ) = s . CreateNewStore ( ) ;
const string cryptoCode = "BTC" ;
// 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 ( cryptoCode , "" , true ) ;
2022-07-04 14:59:46 +02:00
//let's test quickly the wallet send page
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( $"StoreNav-Wallet{cryptoCode}" ) ) . Click ( ) ;
2022-02-17 10:07:41 +01:00
s . Driver . FindElement ( By . Id ( "WalletNav-Send" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
//you cannot use the Sign with NBX option without saving private keys when generating the wallet.
Assert . DoesNotContain ( "nbx-seed" , s . Driver . PageSource ) ;
2022-07-04 14:59:46 +02:00
Assert . True ( s . Driver . ElementDoesNotExist ( By . Id ( "GoBack" ) ) ) ;
s . Driver . FindElement ( By . Id ( "SignTransaction" ) ) . Click ( ) ;
Assert . Contains ( "Destination Address field is required" , s . Driver . PageSource ) ;
Assert . True ( s . Driver . ElementDoesNotExist ( By . Id ( "GoBack" ) ) ) ;
2022-07-04 06:20:08 +02:00
s . Driver . FindElement ( By . Id ( "CancelWizard" ) ) . Click ( ) ;
2022-02-17 10:07:41 +01:00
s . Driver . FindElement ( By . Id ( "WalletNav-Receive" ) ) . Click ( ) ;
2023-01-06 14:18:07 +01:00
2022-01-14 09:50:29 +01:00
//generate a receiving address
s . Driver . FindElement ( By . CssSelector ( "button[value=generate-new-address]" ) ) . Click ( ) ;
2022-03-11 10:43:31 +01:00
Assert . True ( s . Driver . FindElement ( By . CssSelector ( "#address-tab .qr-container" ) ) . Displayed ) ;
2022-07-04 14:59:46 +02:00
// no previous page in the wizard, hence no back button
Assert . True ( s . Driver . ElementDoesNotExist ( By . Id ( "GoBack" ) ) ) ;
2023-09-19 02:56:11 +02:00
var receiveAddr = s . Driver . FindElement ( By . Id ( "Address" ) ) . GetAttribute ( "data-text" ) ;
2023-02-22 03:47:02 +01:00
// Can add a label?
await TestUtils . EventuallyAsync ( async ( ) = >
{
2023-03-26 13:42:38 +02:00
s . Driver . WaitForElement ( By . CssSelector ( "div.label-manager input" ) ) . Click ( ) ;
2023-02-22 03:47:02 +01:00
await Task . Delay ( 500 ) ;
2023-03-26 13:42:38 +02:00
s . Driver . WaitForElement ( By . CssSelector ( "div.label-manager input" ) ) . SendKeys ( "test-label" + Keys . Enter ) ;
2023-02-22 03:47:02 +01:00
await Task . Delay ( 500 ) ;
2023-03-26 13:42:38 +02:00
s . Driver . WaitForElement ( By . CssSelector ( "div.label-manager input" ) ) . SendKeys ( "label2" + Keys . Enter ) ;
2023-02-22 03:47:02 +01:00
} ) ;
2023-04-10 04:07:03 +02:00
2023-02-22 03:47:02 +01:00
TestUtils . Eventually ( ( ) = >
2023-04-10 04:07:03 +02:00
{
2023-02-22 03:47:02 +01:00
s . Driver . Navigate ( ) . Refresh ( ) ;
Assert . NotNull ( s . Driver . FindElement ( By . CssSelector ( "[data-value='test-label']" ) ) ) ;
} ) ;
2022-07-04 14:59:46 +02:00
2022-01-14 09:50:29 +01:00
//unreserve
s . Driver . FindElement ( By . CssSelector ( "button[value=unreserve-current-address]" ) ) . Click ( ) ;
//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 ( ) ;
2022-03-11 10:43:31 +01:00
Assert . True ( s . Driver . FindElement ( By . CssSelector ( "#address-tab .qr-container" ) ) . Displayed ) ;
2023-09-19 02:56:11 +02:00
Assert . Equal ( receiveAddr , s . Driver . FindElement ( By . Id ( "Address" ) ) . GetAttribute ( "data-text" ) ) ;
2023-02-22 03:47:02 +01:00
TestUtils . Eventually ( ( ) = >
{
Assert . Contains ( "test-label" , s . Driver . PageSource ) ;
} ) ;
// Let's try to remove a label
await TestUtils . EventuallyAsync ( async ( ) = >
{
s . Driver . WaitForElement ( By . CssSelector ( "[data-value='test-label']" ) ) . Click ( ) ;
await Task . Delay ( 500 ) ;
s . Driver . ExecuteJavaScript ( "document.querySelector('[data-value=\"test-label\"]').nextSibling.dispatchEvent(new KeyboardEvent('keydown', {'key': 'Delete', keyCode: 46}));" ) ;
2023-04-10 04:07:03 +02:00
2023-02-22 03:47:02 +01:00
} ) ;
TestUtils . Eventually ( ( ) = >
2023-04-10 04:07:03 +02:00
{
2023-02-22 03:47:02 +01:00
s . Driver . Navigate ( ) . Refresh ( ) ;
Assert . DoesNotContain ( "test-label" , s . Driver . PageSource ) ;
} ) ;
2022-07-04 14:59:46 +02:00
Assert . True ( s . Driver . ElementDoesNotExist ( By . Id ( "GoBack" ) ) ) ;
2022-01-14 09:50:29 +01:00
//send money to addr and ensure it changed
var sess = await s . Server . ExplorerClient . CreateWebsocketNotificationSessionAsync ( ) ;
await sess . ListenAllTrackedSourceAsync ( ) ;
var nextEvent = sess . NextEventAsync ( ) ;
await s . Server . ExplorerNode . SendToAddressAsync ( 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 ( ) ;
2023-09-19 02:56:11 +02:00
Assert . NotEqual ( receiveAddr , s . Driver . FindElement ( By . Id ( "Address" ) ) . GetAttribute ( "data-text" ) ) ;
receiveAddr = s . Driver . FindElement ( By . Id ( "Address" ) ) . GetAttribute ( "data-text" ) ;
2022-07-04 06:20:08 +02:00
s . Driver . FindElement ( By . Id ( "CancelWizard" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
2023-02-22 03:47:02 +01:00
// Check the label is applied to the tx
2023-06-22 09:09:53 +02:00
s . Driver . WaitWalletTransactionsLoaded ( ) ;
2023-02-26 03:01:46 +01:00
Assert . Equal ( "label2" , s . Driver . FindElement ( By . XPath ( "//*[@id=\"WalletTransactionsList\"]//*[contains(@class, 'transaction-label')]" ) ) . Text ) ;
2023-02-22 03:47:02 +01:00
2022-01-14 09:50:29 +01:00
//change the wallet and ensure old address is not there and generating a new one does not result in the prev one
s . GenerateWallet ( cryptoCode , "" , true ) ;
2022-01-19 12:58:02 +01:00
s . GoToWallet ( null , WalletsNavPages . Receive ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . CssSelector ( "button[value=generate-new-address]" ) ) . Click ( ) ;
2023-09-19 02:56:11 +02:00
Assert . NotEqual ( receiveAddr , s . Driver . FindElement ( By . Id ( "Address" ) ) . GetAttribute ( "data-text" ) ) ;
2022-01-14 09:50:29 +01:00
var invoiceId = s . CreateInvoice ( 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 ) ;
2022-01-20 12:52:31 +01:00
s . GoToStore ( storeId ) ;
2022-01-14 09:50:29 +01:00
var mnemonic = s . GenerateWallet ( cryptoCode , "" , true , true ) ;
//lets import and save private keys
invoiceId = s . CreateInvoice ( 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.0 m ) ) ;
await s . Server . ExplorerNode . GenerateAsync ( 1 ) ;
s . GoToStore ( storeId ) ;
s . Driver . FindElement ( By . Id ( $"StoreNav-Wallet{cryptoCode}" ) ) . Click ( ) ;
s . ClickOnAllSectionLinks ( ) ;
// Make sure wallet info is correct
2022-01-19 12:58:02 +01:00
s . GoToWalletSettings ( cryptoCode ) ;
2022-01-14 09:50:29 +01:00
Assert . Contains ( mnemonic . DeriveExtKey ( ) . GetPublicKey ( ) . GetHDFingerPrint ( ) . ToString ( ) ,
s . Driver . FindElement ( By . Id ( "AccountKeys_0__MasterFingerprint" ) ) . GetAttribute ( "value" ) ) ;
Assert . Contains ( "m/84'/1'/0'" ,
s . Driver . FindElement ( By . Id ( "AccountKeys_0__AccountKeyPath" ) ) . GetAttribute ( "value" ) ) ;
// Make sure we can rescan, because we are admin!
2022-02-17 10:07:41 +01:00
s . Driver . FindElement ( By . Id ( "ActionsDropdownToggle" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Rescan" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
Assert . Contains ( "The batch size make sure" , s . Driver . PageSource ) ;
// Check the tx sent earlier arrived
2022-02-17 10:07:41 +01:00
s . Driver . FindElement ( By . Id ( $"StoreNav-Wallet{cryptoCode}" ) ) . Click ( ) ;
2023-06-22 09:09:53 +02:00
s . Driver . WaitWalletTransactionsLoaded ( ) ;
s . Driver . FindElement ( By . PartialLinkText ( tx . ToString ( ) ) ) ;
2022-07-04 14:59:46 +02:00
var walletTransactionUri = new Uri ( s . Driver . Url ) ;
2022-01-14 09:50:29 +01:00
// Send to bob
2022-02-17 10:07:41 +01:00
s . Driver . FindElement ( By . Id ( "WalletNav-Send" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
var bob = new Key ( ) . PubKey . Hash . GetAddress ( Network . RegTest ) ;
SetTransactionOutput ( s , 0 , bob , 1 ) ;
s . Driver . FindElement ( By . Id ( "SignTransaction" ) ) . Click ( ) ;
2022-07-04 14:59:46 +02:00
// Back button should lead back to the previous page inside the send wizard
var backUrl = s . Driver . FindElement ( By . Id ( "GoBack" ) ) . GetAttribute ( "href" ) ;
Assert . EndsWith ( $"/send?returnUrl={walletTransactionUri.AbsolutePath}" , backUrl ) ;
// Cancel button should lead to the page that referred to the send wizard
var cancelUrl = s . Driver . FindElement ( By . Id ( "CancelWizard" ) ) . GetAttribute ( "href" ) ;
Assert . EndsWith ( walletTransactionUri . AbsolutePath , cancelUrl ) ;
2022-01-14 09:50:29 +01:00
// Broadcast
Assert . Contains ( bob . ToString ( ) , s . Driver . PageSource ) ;
Assert . Contains ( "1.00000000" , s . Driver . PageSource ) ;
s . Driver . FindElement ( By . CssSelector ( "button[value=broadcast]" ) ) . Click ( ) ;
2022-07-04 14:59:46 +02:00
Assert . Equal ( walletTransactionUri . ToString ( ) , s . Driver . Url ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( $"StoreNav-Wallet{cryptoCode}" ) ) . Click ( ) ;
2022-02-17 10:07:41 +01:00
s . Driver . FindElement ( By . Id ( "WalletNav-Send" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
var jack = new Key ( ) . PubKey . Hash . GetAddress ( Network . RegTest ) ;
SetTransactionOutput ( s , 0 , jack , 0.01 m ) ;
s . Driver . FindElement ( By . Id ( "SignTransaction" ) ) . Click ( ) ;
Assert . Contains ( jack . ToString ( ) , s . Driver . PageSource ) ;
Assert . Contains ( "0.01000000" , s . Driver . PageSource ) ;
Assert . EndsWith ( "psbt/ready" , s . Driver . Url ) ;
s . Driver . FindElement ( By . CssSelector ( "button[value=broadcast]" ) ) . Click ( ) ;
2022-07-04 14:59:46 +02:00
Assert . Equal ( walletTransactionUri . ToString ( ) , s . Driver . Url ) ;
2022-01-14 09:50:29 +01:00
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 ( $"StoreNav-Wallet{cryptoCode}" ) ) . Click ( ) ;
2022-02-17 10:07:41 +01:00
s . Driver . FindElement ( By . Id ( "WalletNav-Send" ) ) . Click ( ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "bip21parse" ) ) . Click ( ) ;
s . Driver . SwitchTo ( ) . Alert ( ) . SendKeys ( bip21 ) ;
s . Driver . SwitchTo ( ) . Alert ( ) . Accept ( ) ;
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" ) ) ;
2022-07-04 06:20:08 +02:00
s . Driver . FindElement ( By . Id ( "CancelWizard" ) ) . Click ( ) ;
2022-01-19 12:58:02 +01:00
s . GoToWalletSettings ( cryptoCode ) ;
2022-07-04 14:59:46 +02:00
var settingsUri = new Uri ( s . Driver . Url ) ;
2022-01-14 09:50:29 +01:00
s . Driver . FindElement ( By . Id ( "ActionsDropdownToggle" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "ViewSeed" ) ) . Click ( ) ;
// Seed backup page
var recoveryPhrase = s . Driver . FindElements ( By . Id ( "RecoveryPhrase" ) ) . First ( )
. GetAttribute ( "data-mnemonic" ) ;
Assert . Equal ( mnemonic . ToString ( ) , recoveryPhrase ) ;
Assert . Contains ( "The recovery phrase will also be stored on the server as a hot wallet." ,
s . Driver . PageSource ) ;
// No confirmation, just a link to return to the wallet
Assert . Empty ( s . Driver . FindElements ( By . Id ( "confirm" ) ) ) ;
s . Driver . FindElement ( By . Id ( "proceed" ) ) . Click ( ) ;
2022-07-04 14:59:46 +02:00
Assert . Equal ( settingsUri . ToString ( ) , s . Driver . Url ) ;
2023-01-06 14:18:07 +01:00
2022-07-04 14:59:46 +02:00
// Once more, test the cancel link of the wallet send page leads back to the previous page
s . Driver . FindElement ( By . Id ( "WalletNav-Send" ) ) . Click ( ) ;
cancelUrl = s . Driver . FindElement ( By . Id ( "CancelWizard" ) ) . GetAttribute ( "href" ) ;
Assert . EndsWith ( settingsUri . AbsolutePath , cancelUrl ) ;
// no previous page in the wizard, hence no back button
Assert . True ( s . Driver . ElementDoesNotExist ( By . Id ( "GoBack" ) ) ) ;
s . Driver . FindElement ( By . Id ( "CancelWizard" ) ) . Click ( ) ;
2023-11-02 08:12:28 +01:00
Assert . Equal ( settingsUri . ToString ( ) , s . Driver . Url ) ;
// Transactions list contains export, ensure functions are present.
2022-05-20 02:35:31 +02:00
s . Driver . FindElement ( By . Id ( $"StoreNav-Wallet{cryptoCode}" ) ) . Click ( ) ;
2023-11-02 08:12:28 +01:00
s . Driver . FindElement ( By . ClassName ( "mass-action-select-all" ) ) . Click ( ) ;
2022-05-20 02:35:31 +02:00
s . Driver . FindElement ( By . Id ( "BumpFee" ) ) ;
2023-11-02 08:12:28 +01:00
2022-05-20 02:35:31 +02:00
// JSON export
s . Driver . FindElement ( By . Id ( "ExportDropdownToggle" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "ExportJSON" ) ) . Click ( ) ;
Thread . Sleep ( 1000 ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
Assert . Contains ( s . WalletId . ToString ( ) , s . Driver . Url ) ;
Assert . EndsWith ( "export?format=json" , s . Driver . Url ) ;
Assert . Contains ( "\"Amount\": \"3.00000000\"" , s . Driver . PageSource ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2023-01-06 14:18:07 +01:00
2023-11-20 02:46:36 +01:00
// CSV export
2023-03-27 06:59:33 +02:00
s . Driver . FindElement ( By . Id ( "ExportDropdownToggle" ) ) . Click ( ) ;
2023-11-20 02:46:36 +01:00
s . Driver . FindElement ( By . Id ( "ExportCSV" ) ) . Click ( ) ;
2023-03-27 06:59:33 +02:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2023-11-20 02:46:36 +01:00
// BIP-329 export
2022-05-20 02:35:31 +02:00
s . Driver . FindElement ( By . Id ( "ExportDropdownToggle" ) ) . Click ( ) ;
2023-11-20 02:46:36 +01:00
s . Driver . FindElement ( By . Id ( "ExportBIP329" ) ) . Click ( ) ;
2023-03-27 06:59:33 +02:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2019-05-12 10:13:26 +02:00
}
2020-06-24 03:34:09 +02:00
2021-08-03 07:27:04 +02:00
[Fact(Timeout = TestTimeout)]
public async Task CanImportWallet ( )
{
2022-01-14 09:50:29 +01:00
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
2022-01-19 12:58:02 +01:00
s . CreateNewStore ( ) ;
2022-01-14 09:50:29 +01:00
const string cryptoCode = "BTC" ;
var mnemonic = s . GenerateWallet ( cryptoCode , "click chunk owner kingdom faint steak safe evidence bicycle repeat bulb wheel" ) ;
// Make sure wallet info is correct
2022-01-19 12:58:02 +01:00
s . GoToWalletSettings ( cryptoCode ) ;
2022-01-14 09:50:29 +01:00
Assert . Contains ( mnemonic . DeriveExtKey ( ) . GetPublicKey ( ) . GetHDFingerPrint ( ) . ToString ( ) ,
s . Driver . FindElement ( By . Id ( "AccountKeys_0__MasterFingerprint" ) ) . GetAttribute ( "value" ) ) ;
Assert . Contains ( "m/84'/1'/0'" ,
s . Driver . FindElement ( By . Id ( "AccountKeys_0__AccountKeyPath" ) ) . GetAttribute ( "value" ) ) ;
2023-01-06 14:18:07 +01:00
2022-05-20 02:35:31 +02:00
// Transactions list is empty
s . Driver . FindElement ( By . Id ( $"StoreNav-Wallet{cryptoCode}" ) ) . Click ( ) ;
2023-06-22 09:09:53 +02:00
s . Driver . WaitWalletTransactionsLoaded ( ) ;
Assert . Contains ( "There are no transactions yet" , s . Driver . FindElement ( By . Id ( "WalletTransactions" ) ) . Text ) ;
2021-08-03 07:27:04 +02:00
}
2022-08-11 14:30:42 +02:00
[Fact]
[Trait("Selenium", "Selenium")]
[Trait("Lightning", "Lightning")]
public async Task CanEditPullPaymentUI ( )
{
using var s = CreateSeleniumTester ( ) ;
s . Server . ActivateLightning ( LightningConnectionType . LndREST ) ;
await s . StartAsync ( ) ;
await s . Server . EnsureChannelsSetup ( ) ;
s . RegisterNewUser ( true ) ;
s . CreateNewStore ( ) ;
s . GenerateWallet ( "BTC" , "" , true , true ) ;
await s . Server . ExplorerNode . GenerateAsync ( 1 ) ;
await s . FundStoreWallet ( denomination : 50.0 m ) ;
s . GoToStore ( s . StoreId , StoreNavPages . 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" ) ;
s . Driver . FindElement ( By . Id ( "Create" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
2022-08-11 14:30:42 +02:00
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
Assert . Contains ( "PP1" , s . Driver . PageSource ) ;
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2022-08-11 14:30:42 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
s . Driver . FindElement ( By . LinkText ( "PP1" ) ) . Click ( ) ;
var name = s . Driver . FindElement ( By . Id ( "Name" ) ) ;
name . Clear ( ) ;
name . SendKeys ( "PP1 Edited" ) ;
var description = s . Driver . FindElement ( By . ClassName ( "card-block" ) ) ;
description . SendKeys ( "Description Edit" ) ;
s . Driver . FindElement ( By . Id ( "SaveButton" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2022-08-11 14:30:42 +02:00
Assert . Contains ( "Description Edit" , s . Driver . PageSource ) ;
Assert . Contains ( "PP1 Edited" , s . Driver . PageSource ) ;
}
2020-06-24 03:34:09 +02:00
[Fact]
[Trait("Selenium", "Selenium")]
2021-10-18 05:37:59 +02:00
[Trait("Lightning", "Lightning")]
2020-06-24 03:34:09 +02:00
public async Task CanUsePullPaymentsViaUI ( )
{
2021-11-22 09:16:08 +01:00
using var s = CreateSeleniumTester ( ) ;
2021-11-26 07:02:30 +01:00
s . Server . ActivateLightning ( LightningConnectionType . LndREST ) ;
2021-06-30 09:59:01 +02:00
await s . StartAsync ( ) ;
2021-11-26 07:02:30 +01:00
await s . Server . EnsureChannelsSetup ( ) ;
2021-06-30 09:59:01 +02:00
s . RegisterNewUser ( true ) ;
s . CreateNewStore ( ) ;
s . GenerateWallet ( "BTC" , "" , true , true ) ;
await s . Server . ExplorerNode . GenerateAsync ( 1 ) ;
await s . FundStoreWallet ( denomination : 50.0 m ) ;
2021-10-22 04:17:40 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
2021-06-30 09:59:01 +02:00
s . Driver . FindElement ( By . Id ( "NewPullPayment" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Name" ) ) . SendKeys ( "PP1" ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . Clear ( ) ;
2021-10-29 11:01:16 +02:00
s . Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( "99.0" ) ;
2021-06-30 09:59:01 +02:00
s . Driver . FindElement ( By . Id ( "Create" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
2021-06-30 09:59:01 +02:00
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
Assert . Contains ( "PP1" , s . Driver . PageSource ) ;
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2021-06-30 09:59:01 +02:00
2021-10-22 04:17:40 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
2021-06-30 09:59:01 +02:00
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" ) ;
s . Driver . FindElement ( By . Id ( "Create" ) ) . Click ( ) ;
// This should select the first View, ie, the last one PP2
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2021-06-30 09:59:01 +02:00
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 . 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 . 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 . FindAlertMessage ( ) ;
Assert . Contains ( "Awaiting Approval" , s . Driver . PageSource ) ;
var viewPullPaymentUrl = s . Driver . Url ;
2023-11-20 02:45:43 +01:00
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2021-06-30 09:59:01 +02:00
// This one should have nothing
2021-10-22 04:17:40 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
2021-06-30 09:59:01 +02:00
var payouts = s . Driver . FindElements ( By . ClassName ( "pp-payout" ) ) ;
Assert . Equal ( 2 , payouts . Count ) ;
payouts [ 1 ] . Click ( ) ;
Assert . Empty ( s . Driver . FindElements ( By . ClassName ( "payout" ) ) ) ;
// PP2 should have payouts
2021-10-22 04:17:40 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
2021-06-30 09:59:01 +02:00
payouts = s . Driver . FindElements ( By . ClassName ( "pp-payout" ) ) ;
payouts [ 0 ] . Click ( ) ;
2021-10-29 11:01:16 +02:00
2021-06-30 09:59:01 +02:00
Assert . NotEmpty ( s . Driver . FindElements ( By . ClassName ( "payout" ) ) ) ;
2023-11-02 08:12:28 +01:00
s . Driver . FindElement ( By . ClassName ( "mass-action-select-all" ) ) . Click ( ) ;
2021-06-30 09:59:01 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.AwaitingApproval}-approve-pay" ) ) . Click ( ) ;
2021-05-19 04:39:27 +02:00
2021-06-30 09:59:01 +02:00
s . Driver . FindElement ( By . Id ( "SignTransaction" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( "button[value=broadcast]" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
2020-06-24 03:34:09 +02:00
2022-07-04 06:20:08 +02:00
s . GoToWallet ( null , WalletsNavPages . Transactions ) ;
2021-06-30 09:59:01 +02:00
TestUtils . Eventually ( ( ) = >
{
s . Driver . Navigate ( ) . Refresh ( ) ;
2023-09-22 10:24:53 +02:00
s . Driver . WaitWalletTransactionsLoaded ( ) ;
2023-02-26 03:01:46 +01:00
Assert . Contains ( "transaction-label" , s . Driver . PageSource ) ;
2023-05-26 16:49:32 +02:00
var labels = s . Driver . FindElements ( By . CssSelector ( "#WalletTransactionsList tr:first-child div.transaction-label" ) ) ;
Assert . Equal ( 2 , labels . Count ) ;
Assert . Contains ( labels , element = > element . Text = = "payout" ) ;
Assert . Contains ( labels , element = > element . Text = = "pull-payment" ) ;
2021-06-30 09:59:01 +02:00
} ) ;
2023-05-26 16:49:32 +02:00
2021-10-22 04:17:40 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . Payouts ) ;
2021-06-30 09:59:01 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.InProgress}-view" ) ) . Click ( ) ;
ReadOnlyCollection < IWebElement > txs ;
TestUtils . Eventually ( ( ) = >
{
s . Driver . Navigate ( ) . Refresh ( ) ;
2021-10-29 11:01:16 +02:00
2020-06-24 03:34:09 +02:00
txs = s . Driver . FindElements ( By . ClassName ( "transaction-link" ) ) ;
Assert . Equal ( 2 , txs . Count ) ;
2021-06-30 09:59:01 +02:00
} ) ;
2020-06-24 03:34:09 +02:00
2021-06-30 09:59:01 +02:00
s . Driver . Navigate ( ) . GoToUrl ( viewPullPaymentUrl ) ;
txs = s . Driver . FindElements ( By . ClassName ( "transaction-link" ) ) ;
Assert . Equal ( 2 , txs . Count ) ;
Assert . Contains ( PayoutState . InProgress . GetStateString ( ) , s . Driver . PageSource ) ;
2020-06-24 03:34:09 +02:00
2021-06-30 09:59:01 +02:00
await s . Server . ExplorerNode . GenerateAsync ( 1 ) ;
2020-06-24 03:34:09 +02:00
2021-06-30 09:59:01 +02:00
TestUtils . Eventually ( ( ) = >
{
s . Driver . Navigate ( ) . Refresh ( ) ;
Assert . Contains ( PayoutState . Completed . GetStateString ( ) , 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 = = PayoutState . Completed ) ) ;
} ) ;
2021-07-16 09:57:37 +02:00
s . GoToHome ( ) ;
//offline/external payout test
2023-09-26 15:50:04 +02:00
2021-07-16 09:57:37 +02:00
var newStore = s . CreateNewStore ( ) ;
s . GenerateWallet ( "BTC" , "" , true , true ) ;
2021-10-22 04:17:40 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
2021-10-29 11:01:16 +02:00
2021-07-16 09:57:37 +02:00
s . Driver . FindElement ( By . Id ( "NewPullPayment" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Name" ) ) . SendKeys ( "External Test" ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( "0.001" ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . SendKeys ( "BTC" ) ;
s . Driver . FindElement ( By . Id ( "Create" ) ) . Click ( ) ;
2021-10-29 11:01:16 +02:00
2023-11-20 02:45:43 +01:00
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2021-07-16 09:57:37 +02:00
address = await s . Server . ExplorerNode . GetNewAddressAsync ( ) ;
s . Driver . FindElement ( By . Id ( "Destination" ) ) . SendKeys ( address . ToString ( ) ) ;
s . Driver . FindElement ( By . Id ( "ClaimedAmount" ) ) . SendKeys ( Keys . Enter ) ;
s . FindAlertMessage ( ) ;
Assert . Contains ( PayoutState . AwaitingApproval . GetStateString ( ) , s . Driver . PageSource ) ;
2021-10-22 04:17:40 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . Payouts ) ;
2021-07-16 09:57:37 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.AwaitingApproval}-view" ) ) . Click ( ) ;
2023-11-02 08:12:28 +01:00
s . Driver . FindElement ( By . ClassName ( "mass-action-select-all" ) ) . Click ( ) ;
2021-07-16 09:57:37 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.AwaitingApproval}-approve" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
2021-10-29 11:01:16 +02:00
var tx = await s . Server . ExplorerNode . SendToAddressAsync ( address , Money . FromUnit ( 0.001 m , MoneyUnit . BTC ) ) ;
2023-11-20 02:45:43 +01:00
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2021-07-16 09:57:37 +02:00
2021-10-22 04:17:40 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . Payouts ) ;
2021-10-29 11:01:16 +02:00
2021-07-16 09:57:37 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.AwaitingPayment}-view" ) ) . Click ( ) ;
Assert . Contains ( PayoutState . AwaitingPayment . GetStateString ( ) , s . Driver . PageSource ) ;
2023-11-02 08:12:28 +01:00
s . Driver . FindElement ( By . ClassName ( "mass-action-select-all" ) ) . Click ( ) ;
2021-08-05 07:47:25 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.AwaitingPayment}-mark-paid" ) ) . Click ( ) ;
2021-07-16 09:57:37 +02:00
s . FindAlertMessage ( ) ;
2021-10-29 11:01:16 +02:00
2021-10-18 05:37:59 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.InProgress}-view" ) ) . Click ( ) ;
2021-07-16 09:57:37 +02:00
Assert . Contains ( tx . ToString ( ) , s . Driver . PageSource ) ;
2021-11-26 07:02:30 +01:00
2021-10-18 05:37:59 +02:00
//lightning tests
2021-11-26 07:02:30 +01:00
// Since the merchant is sending on lightning, it needs some liquidity from the client
var payoutAmount = LightMoney . Satoshis ( 1000 ) ;
var minimumReserve = LightMoney . Satoshis ( 167773 m ) ;
var inv = await s . Server . MerchantLnd . Client . CreateInvoice ( minimumReserve + payoutAmount , "Donation to merchant" , TimeSpan . FromHours ( 1 ) , default ) ;
var resp = await s . Server . CustomerLightningD . Pay ( inv . BOLT11 ) ;
Assert . Equal ( PayResult . Ok , resp . Result ) ;
2021-10-18 05:37:59 +02:00
newStore = s . CreateNewStore ( ) ;
2021-12-31 14:02:53 +01:00
s . AddLightningNode ( ) ;
2023-01-06 14:18:07 +01:00
2021-10-29 08:25:43 +02:00
//Currently an onchain wallet is required to use the Lightning payouts feature..
2021-10-18 05:37:59 +02:00
s . GenerateWallet ( "BTC" , "" , true , true ) ;
2021-10-22 04:17:40 +02:00
s . GoToStore ( newStore . storeId , StoreNavPages . PullPayments ) ;
2021-10-18 05:37:59 +02:00
s . Driver . FindElement ( By . Id ( "NewPullPayment" ) ) . Click ( ) ;
2021-11-16 04:58:17 +01:00
var paymentMethodOptions = s . Driver . FindElements ( By . CssSelector ( "input[name='PaymentMethods']" ) ) ;
2021-10-18 05:37:59 +02:00
Assert . Equal ( 2 , paymentMethodOptions . Count ) ;
2021-12-31 08:59:02 +01:00
2021-10-18 05:37:59 +02:00
s . Driver . FindElement ( By . Id ( "Name" ) ) . SendKeys ( "Lightning Test" ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . Clear ( ) ;
2021-11-26 07:02:30 +01:00
s . Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( payoutAmount . ToString ( ) ) ;
2021-10-18 05:37:59 +02:00
s . Driver . FindElement ( By . Id ( "Currency" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . SendKeys ( "BTC" ) ;
s . Driver . FindElement ( By . Id ( "Create" ) ) . Click ( ) ;
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2023-10-10 05:30:09 +02:00
// Bitcoin-only, SelectedPaymentMethod should not be displayed
s . Driver . ElementDoesNotExist ( By . Id ( "SelectedPaymentMethod" ) ) ;
2021-10-18 05:37:59 +02:00
2021-11-26 07:02:30 +01:00
var bolt = ( await s . Server . CustomerLightningD . CreateInvoice (
payoutAmount ,
2021-12-17 07:31:06 +01:00
$"LN payout test {DateTime.UtcNow.Ticks}" ,
2021-10-18 05:37:59 +02:00
TimeSpan . FromHours ( 1 ) , CancellationToken . None ) ) . BOLT11 ;
s . Driver . FindElement ( By . Id ( "Destination" ) ) . SendKeys ( bolt ) ;
s . Driver . FindElement ( By . Id ( "ClaimedAmount" ) ) . SendKeys ( Keys . Enter ) ;
//we do not allow short-life bolts.
s . FindAlertMessage ( StatusMessageModel . StatusSeverity . Error ) ;
2021-11-26 07:02:30 +01:00
bolt = ( await s . Server . CustomerLightningD . CreateInvoice (
payoutAmount ,
2021-12-17 07:31:06 +01:00
$"LN payout test {DateTime.UtcNow.Ticks}" ,
2021-10-18 05:37:59 +02:00
TimeSpan . FromDays ( 31 ) , CancellationToken . None ) ) . BOLT11 ;
s . Driver . FindElement ( By . Id ( "Destination" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Destination" ) ) . SendKeys ( bolt ) ;
s . Driver . FindElement ( By . Id ( "ClaimedAmount" ) ) . SendKeys ( Keys . Enter ) ;
s . FindAlertMessage ( ) ;
Assert . Contains ( PayoutState . AwaitingApproval . GetStateString ( ) , s . Driver . PageSource ) ;
2023-11-20 02:45:43 +01:00
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2021-10-22 04:17:40 +02:00
s . GoToStore ( newStore . storeId , StoreNavPages . Payouts ) ;
2021-10-29 11:01:16 +02:00
s . Driver . FindElement ( By . Id ( $"{new PaymentMethodId(" BTC ", PaymentTypes.LightningLike)}-view" ) ) . Click ( ) ;
2021-10-18 05:37:59 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.AwaitingApproval}-view" ) ) . Click ( ) ;
2023-11-02 08:12:28 +01:00
s . Driver . FindElement ( By . ClassName ( "mass-action-select-all" ) ) . Click ( ) ;
2021-10-18 05:37:59 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.AwaitingApproval}-approve-pay" ) ) . Click ( ) ;
Assert . Contains ( bolt , s . Driver . PageSource ) ;
2023-11-20 02:45:43 +01:00
Assert . Contains ( $"{payoutAmount} BTC" , s . Driver . PageSource ) ;
2021-10-18 05:37:59 +02:00
s . Driver . FindElement ( By . CssSelector ( "#pay-invoices-form" ) ) . Submit ( ) ;
2021-11-26 07:02:30 +01:00
2023-11-20 02:45:43 +01:00
s . FindAlertMessage ( ) ;
2021-10-22 04:17:40 +02:00
s . GoToStore ( newStore . storeId , StoreNavPages . Payouts ) ;
2021-10-29 11:01:16 +02:00
s . Driver . FindElement ( By . Id ( $"{new PaymentMethodId(" BTC ", PaymentTypes.LightningLike)}-view" ) ) . Click ( ) ;
2021-10-18 05:37:59 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.Completed}-view" ) ) . Click ( ) ;
if ( ! s . Driver . PageSource . Contains ( bolt ) )
{
s . Driver . FindElement ( By . Id ( $"{PayoutState.AwaitingPayment}-view" ) ) . Click ( ) ;
Assert . Contains ( bolt , s . Driver . PageSource ) ;
2021-10-29 11:01:16 +02:00
2023-11-02 08:12:28 +01:00
s . Driver . FindElement ( By . ClassName ( "mass-action-select-all" ) ) . Click ( ) ;
2021-10-18 05:37:59 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.AwaitingPayment}-mark-paid" ) ) . Click ( ) ;
2021-10-29 11:01:16 +02:00
s . Driver . FindElement ( By . Id ( $"{new PaymentMethodId(" BTC ", PaymentTypes.LightningLike)}-view" ) ) . Click ( ) ;
2021-10-18 05:37:59 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.Completed}-view" ) ) . Click ( ) ;
Assert . Contains ( bolt , s . Driver . PageSource ) ;
}
2023-01-06 14:18:07 +01:00
2022-04-28 02:51:04 +02:00
//auto-approve pull payments
s . GoToStore ( StoreNavPages . PullPayments ) ;
s . Driver . FindElement ( By . Id ( "NewPullPayment" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Name" ) ) . SendKeys ( "PP1" ) ;
s . Driver . SetCheckbox ( By . Id ( "AutoApproveClaims" ) , true ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( "99.0" + Keys . Enter ) ;
2023-11-20 02:45:43 +01:00
s . FindAlertMessage ( ) ;
2022-04-28 02:51:04 +02:00
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2022-04-28 02:51:04 +02:00
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 ) ;
2023-11-20 02:45:43 +01:00
s . FindAlertMessage ( ) ;
2022-04-28 02:51:04 +02:00
Assert . Contains ( PayoutState . AwaitingPayment . GetStateString ( ) , s . Driver . PageSource ) ;
2023-11-20 02:45:43 +01:00
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2022-04-28 02:51:04 +02:00
2023-06-16 03:56:17 +02:00
// LNURL Withdraw support check with BTC denomination
2023-01-06 14:18:07 +01:00
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
2022-06-28 16:02:17 +02:00
s . Driver . FindElement ( By . Id ( "NewPullPayment" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Name" ) ) . SendKeys ( "PP1" ) ;
s . Driver . SetCheckbox ( By . Id ( "AutoApproveClaims" ) , true ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( "0.0000001" ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . SendKeys ( "BTC" + Keys . Enter ) ;
2023-11-20 02:45:43 +01:00
s . FindAlertMessage ( ) ;
2022-06-28 16:02:17 +02:00
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2022-07-12 08:17:44 +02:00
s . Driver . FindElement ( By . CssSelector ( "#lnurlwithdraw-button" ) ) . Click ( ) ;
2023-09-22 10:24:53 +02:00
s . Driver . WaitForElement ( By . Id ( "qr-code-data-input" ) ) ;
2023-12-06 01:17:58 +01:00
// Try to use lnurlw via the QR Code
2023-01-06 14:18:07 +01:00
var lnurl = new Uri ( LNURL . LNURL . Parse ( s . Driver . FindElement ( By . Id ( "qr-code-data-input" ) ) . GetAttribute ( "value" ) , out _ ) . ToString ( ) . Replace ( "https" , "http" ) ) ;
2022-07-12 08:17:44 +02:00
s . Driver . FindElement ( By . CssSelector ( "button[data-bs-dismiss='modal']" ) ) . Click ( ) ;
2022-06-28 16:02:17 +02:00
var info = Assert . IsType < LNURLWithdrawRequest > ( await LNURL . LNURL . FetchInformation ( lnurl , s . Server . PayTester . HttpClient ) ) ;
Assert . Equal ( info . MaxWithdrawable , new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ) ;
Assert . Equal ( info . CurrentBalance , new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ) ;
info = Assert . IsType < LNURLWithdrawRequest > ( await LNURL . LNURL . FetchInformation ( info . BalanceCheck , s . Server . PayTester . HttpClient ) ) ;
Assert . Equal ( info . MaxWithdrawable , new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ) ;
Assert . Equal ( info . CurrentBalance , new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ) ;
2023-01-06 14:18:07 +01:00
2022-06-28 16:02:17 +02:00
var bolt2 = ( await s . Server . CustomerLightningD . CreateInvoice (
2023-12-06 01:17:58 +01:00
new LightMoney ( 0.00000005 m , LightMoneyUnit . BTC ) ,
2022-06-28 16:02:17 +02:00
$"LNurl w payout test {DateTime.UtcNow.Ticks}" ,
TimeSpan . FromHours ( 1 ) , CancellationToken . None ) ) ;
2023-09-22 12:22:30 +02:00
var response = await info . SendRequest ( bolt2 . BOLT11 , s . Server . PayTester . HttpClient , null , null ) ;
2022-06-28 16:02:17 +02:00
await TestUtils . EventuallyAsync ( async ( ) = >
{
s . Driver . Navigate ( ) . Refresh ( ) ;
Assert . Contains ( bolt2 . BOLT11 , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
2022-06-28 16:02:17 +02:00
Assert . Contains ( PayoutState . Completed . GetStateString ( ) , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
Assert . Equal ( LightningInvoiceStatus . Paid , ( await s . Server . CustomerLightningD . GetInvoice ( bolt2 . Id ) ) . Status ) ;
2022-06-28 16:02:17 +02:00
} ) ;
2023-11-20 02:45:43 +01:00
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2023-01-06 14:18:07 +01:00
2023-12-06 01:17:58 +01:00
// Simulate a boltcard
{
var db = s . Server . PayTester . GetService < ApplicationDbContextFactory > ( ) ;
var ppid = lnurl . AbsoluteUri . Split ( "/" ) . Last ( ) ;
var issuerKey = new IssuerKey ( SettingsRepositoryExtensions . FixedKey ( ) ) ;
var uid = RandomNumberGenerator . GetBytes ( 7 ) ;
2023-12-21 02:29:28 +01:00
var cardKey = issuerKey . CreatePullPaymentCardKey ( uid , 0 , ppid ) ;
2023-12-06 01:17:58 +01:00
var keys = cardKey . DeriveBoltcardKeys ( issuerKey ) ;
await db . LinkBoltcardToPullPayment ( ppid , issuerKey , uid ) ;
var piccData = new byte [ ] { 0xc7 } . Concat ( uid ) . Concat ( new byte [ ] { 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 } ) . ToArray ( ) ;
var p = keys . EncryptionKey . Encrypt ( piccData ) ;
var c = keys . AuthenticationKey . GetSunMac ( uid , 1 ) ;
var boltcardUrl = new Uri ( s . Server . PayTester . ServerUri . AbsoluteUri + $"boltcard?p={Encoders.Hex.EncodeData(p).ToStringUpperInvariant()}&c={Encoders.Hex.EncodeData(c).ToStringUpperInvariant()}" ) ;
// p and c should work so long as no bolt11 has been submitted
info = ( LNURLWithdrawRequest ) await LNURL . LNURL . FetchInformation ( boltcardUrl , s . Server . PayTester . HttpClient ) ;
info = ( LNURLWithdrawRequest ) await LNURL . LNURL . FetchInformation ( boltcardUrl , s . Server . PayTester . HttpClient ) ;
var fakeBoltcardUrl = new Uri ( Regex . Replace ( boltcardUrl . AbsoluteUri , "p=([A-F0-9]{32})" , $"p={RandomBytes(16)}" ) ) ;
await Assert . ThrowsAsync < LNUrlException > ( ( ) = > LNURL . LNURL . FetchInformation ( fakeBoltcardUrl , s . Server . PayTester . HttpClient ) ) ;
fakeBoltcardUrl = new Uri ( Regex . Replace ( boltcardUrl . AbsoluteUri , "c=([A-F0-9]{16})" , $"c={RandomBytes(8)}" ) ) ;
await Assert . ThrowsAsync < LNUrlException > ( ( ) = > LNURL . LNURL . FetchInformation ( fakeBoltcardUrl , s . Server . PayTester . HttpClient ) ) ;
bolt2 = ( await s . Server . CustomerLightningD . CreateInvoice (
new LightMoney ( 0.00000005 m , LightMoneyUnit . BTC ) ,
$"LNurl w payout test2 {DateTime.UtcNow.Ticks}" ,
TimeSpan . FromHours ( 1 ) , CancellationToken . None ) ) ;
response = await info . SendRequest ( bolt2 . BOLT11 , s . Server . PayTester . HttpClient , null , null ) ;
Assert . Equal ( "OK" , response . Status ) ;
// No replay should be possible
await Assert . ThrowsAsync < LNUrlException > ( ( ) = > LNURL . LNURL . FetchInformation ( boltcardUrl , s . Server . PayTester . HttpClient ) ) ;
response = await info . SendRequest ( bolt2 . BOLT11 , s . Server . PayTester . HttpClient , null , null ) ;
Assert . Equal ( "ERROR" , response . Status ) ;
Assert . Contains ( "Replayed" , response . Reason ) ;
// Check the state of the registration, counter should have increased
var reg = await db . GetBoltcardRegistration ( issuerKey , uid ) ;
Assert . Equal ( ( ppid , 1 , 0 ) , ( reg . PullPaymentId , reg . Counter , reg . Version ) ) ;
await db . SetBoltcardResetState ( issuerKey , uid ) ;
// After reset, counter is 0, version unchanged and ppId null
reg = await db . GetBoltcardRegistration ( issuerKey , uid ) ;
Assert . Equal ( ( null , 0 , 0 ) , ( reg . PullPaymentId , reg . Counter , reg . Version ) ) ;
await db . LinkBoltcardToPullPayment ( ppid , issuerKey , uid ) ;
// Relink should bump Version
reg = await db . GetBoltcardRegistration ( issuerKey , uid ) ;
Assert . Equal ( ( ppid , 0 , 1 ) , ( reg . PullPaymentId , reg . Counter , reg . Version ) ) ;
2023-12-21 10:16:25 +01:00
await db . LinkBoltcardToPullPayment ( ppid , issuerKey , uid ) ;
reg = await db . GetBoltcardRegistration ( issuerKey , uid ) ;
Assert . Equal ( ( ppid , 0 , 2 ) , ( reg . PullPaymentId , reg . Counter , reg . Version ) ) ;
2023-12-06 01:17:58 +01:00
}
2023-01-06 14:18:07 +01:00
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
2022-06-28 16:02:17 +02:00
s . Driver . FindElement ( By . Id ( "NewPullPayment" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Name" ) ) . SendKeys ( "PP1" ) ;
s . Driver . SetCheckbox ( By . Id ( "AutoApproveClaims" ) , false ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( "0.0000001" ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . SendKeys ( "BTC" + Keys . Enter ) ;
2023-11-20 02:45:43 +01:00
s . FindAlertMessage ( ) ;
2022-06-28 16:02:17 +02:00
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2022-07-12 08:17:44 +02:00
s . Driver . FindElement ( By . CssSelector ( "#lnurlwithdraw-button" ) ) . Click ( ) ;
2023-01-06 14:18:07 +01:00
lnurl = new Uri ( LNURL . LNURL . Parse ( s . Driver . FindElement ( By . Id ( "qr-code-data-input" ) ) . GetAttribute ( "value" ) , out _ ) . ToString ( ) . Replace ( "https" , "http" ) ) ;
2022-07-12 08:17:44 +02:00
s . Driver . FindElement ( By . CssSelector ( "button[data-bs-dismiss='modal']" ) ) . Click ( ) ;
2022-06-28 16:02:17 +02:00
info = Assert . IsType < LNURLWithdrawRequest > ( await LNURL . LNURL . FetchInformation ( lnurl , s . Server . PayTester . HttpClient ) ) ;
Assert . Equal ( info . MaxWithdrawable , new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ) ;
Assert . Equal ( info . CurrentBalance , new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ) ;
info = Assert . IsType < LNURLWithdrawRequest > ( await LNURL . LNURL . FetchInformation ( info . BalanceCheck , s . Server . PayTester . HttpClient ) ) ;
Assert . Equal ( info . MaxWithdrawable , new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ) ;
Assert . Equal ( info . CurrentBalance , new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ) ;
2023-01-06 14:18:07 +01:00
2022-06-28 16:02:17 +02:00
bolt2 = ( await s . Server . CustomerLightningD . CreateInvoice (
new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ,
$"LNurl w payout test {DateTime.UtcNow.Ticks}" ,
TimeSpan . FromHours ( 1 ) , CancellationToken . None ) ) ;
2023-09-22 12:22:30 +02:00
response = await info . SendRequest ( bolt2 . BOLT11 , s . Server . PayTester . HttpClient , null , null ) ;
2022-06-29 16:42:30 +02:00
TestUtils . Eventually ( ( ) = >
2022-06-28 16:02:17 +02:00
{
s . Driver . Navigate ( ) . Refresh ( ) ;
Assert . Contains ( bolt2 . BOLT11 , s . Driver . PageSource ) ;
2023-01-06 14:18:07 +01:00
2022-06-28 16:02:17 +02:00
Assert . Contains ( PayoutState . AwaitingApproval . GetStateString ( ) , s . Driver . PageSource ) ;
} ) ;
2023-11-20 02:45:43 +01:00
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2023-06-16 03:56:17 +02:00
// LNURL Withdraw support check with SATS denomination
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
s . Driver . FindElement ( By . Id ( "NewPullPayment" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Name" ) ) . SendKeys ( "PP SATS" ) ;
s . Driver . SetCheckbox ( By . Id ( "AutoApproveClaims" ) , true ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( "21021" ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . SendKeys ( "SATS" + Keys . Enter ) ;
2023-11-20 02:45:43 +01:00
s . FindAlertMessage ( ) ;
2023-06-16 03:56:17 +02:00
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2023-06-16 03:56:17 +02:00
s . Driver . FindElement ( By . CssSelector ( "#lnurlwithdraw-button" ) ) . Click ( ) ;
lnurl = new Uri ( LNURL . LNURL . Parse ( s . Driver . FindElement ( By . Id ( "qr-code-data-input" ) ) . GetAttribute ( "value" ) , out _ ) . ToString ( ) . Replace ( "https" , "http" ) ) ;
s . Driver . FindElement ( By . CssSelector ( "button[data-bs-dismiss='modal']" ) ) . Click ( ) ;
var amount = new LightMoney ( 21021 , LightMoneyUnit . Satoshi ) ;
info = Assert . IsType < LNURLWithdrawRequest > ( await LNURL . LNURL . FetchInformation ( lnurl , s . Server . PayTester . HttpClient ) ) ;
Assert . Equal ( amount , info . MaxWithdrawable ) ;
Assert . Equal ( amount , info . CurrentBalance ) ;
info = Assert . IsType < LNURLWithdrawRequest > ( await LNURL . LNURL . FetchInformation ( info . BalanceCheck , s . Server . PayTester . HttpClient ) ) ;
Assert . Equal ( amount , info . MaxWithdrawable ) ;
Assert . Equal ( amount , info . CurrentBalance ) ;
bolt2 = ( await s . Server . CustomerLightningD . CreateInvoice (
amount ,
$"LNurl w payout test {DateTime.UtcNow.Ticks}" ,
TimeSpan . FromHours ( 1 ) , CancellationToken . None ) ) ;
2023-09-22 12:22:30 +02:00
response = await info . SendRequest ( bolt2 . BOLT11 , s . Server . PayTester . HttpClient , null , null ) ;
2023-06-16 03:56:17 +02:00
await TestUtils . EventuallyAsync ( async ( ) = >
{
s . Driver . Navigate ( ) . Refresh ( ) ;
Assert . Contains ( bolt2 . BOLT11 , s . Driver . PageSource ) ;
Assert . Contains ( PayoutState . Completed . GetStateString ( ) , s . Driver . PageSource ) ;
Assert . Equal ( LightningInvoiceStatus . Paid , ( await s . Server . CustomerLightningD . GetInvoice ( bolt2 . Id ) ) . Status ) ;
} ) ;
2023-11-20 02:45:43 +01:00
s . Driver . Close ( ) ;
2021-10-26 13:55:13 +02:00
}
2021-10-29 10:27:33 +02:00
2023-12-06 01:17:58 +01:00
private string RandomBytes ( int count )
{
var c = RandomNumberGenerator . GetBytes ( count ) ;
return Encoders . Hex . EncodeData ( c ) ;
}
2021-10-29 10:27:33 +02:00
[Fact]
[Trait("Selenium", "Selenium")]
[Trait("Lightning", "Lightning")]
public async Task CanUsePOSPrint ( )
{
2021-11-22 09:16:08 +01:00
using var s = CreateSeleniumTester ( ) ;
2021-10-29 10:27:33 +02:00
s . Server . ActivateLightning ( ) ;
await s . StartAsync ( ) ;
s . RegisterNewUser ( true ) ;
2021-12-31 14:02:53 +01:00
s . CreateNewStore ( ) ;
2022-01-20 12:52:31 +01:00
s . GoToStore ( ) ;
2021-12-31 14:02:53 +01:00
s . AddLightningNode ( LightningConnectionType . CLightning , false ) ;
s . GoToLightningSettings ( ) ;
2021-10-29 10:27:33 +02:00
s . Driver . SetCheckbox ( By . Id ( "LNURLEnabled" ) , true ) ;
2024-03-14 11:11:54 +01:00
s . CreateApp ( "PointOfSale" ) ;
2023-01-26 01:27:31 +01:00
s . Driver . FindElement ( By . CssSelector ( "label[for='DefaultView_Print']" ) ) . Click ( ) ;
2021-10-29 10:27:33 +02:00
s . Driver . FindElement ( By . Id ( "SaveSettings" ) ) . Click ( ) ;
2021-12-11 04:32:23 +01:00
Assert . Contains ( "App updated" , s . FindAlertMessage ( ) . Text ) ;
2021-10-29 10:27:33 +02:00
s . Driver . FindElement ( By . Id ( "ViewApp" ) ) . Click ( ) ;
var btns = s . Driver . FindElements ( By . ClassName ( "lnurl" ) ) ;
foreach ( IWebElement webElement in btns )
{
var choice = webElement . GetAttribute ( "data-choice" ) ;
var lnurl = webElement . GetAttribute ( "href" ) ;
var parsed = LNURL . LNURL . Parse ( lnurl , out _ ) ;
2021-10-29 14:51:41 +02:00
Assert . EndsWith ( choice , parsed . ToString ( ) ) ;
2021-10-29 10:27:33 +02:00
Assert . IsType < LNURLPayRequest > ( await LNURL . LNURL . FetchInformation ( parsed , new HttpClient ( ) ) ) ;
}
}
2021-10-29 08:25:43 +02:00
[Fact]
2021-10-26 13:55:13 +02:00
[Trait("Selenium", "Selenium")]
2023-06-22 08:57:29 +02:00
public async Task CanUsePOSKeypad ( )
{
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
2024-01-25 13:00:33 +01:00
// Create users
var user = s . RegisterNewUser ( ) ;
var userAccount = s . AsTestAccount ( ) ;
s . GoToHome ( ) ;
s . Logout ( ) ;
s . GoToRegister ( ) ;
2023-06-22 08:57:29 +02:00
s . RegisterNewUser ( true ) ;
2024-01-25 13:00:33 +01:00
// Setup store and associate user
2024-03-14 11:11:54 +01:00
( _ , string storeId ) = s . CreateNewStore ( ) ;
2023-06-22 08:57:29 +02:00
s . GoToStore ( ) ;
2024-03-14 11:11:54 +01:00
s . AddDerivationScheme ( ) ;
s . AddUserToStore ( storeId , user , "Guest" ) ;
2024-01-25 13:00:33 +01:00
// Setup POS
2024-03-14 11:11:54 +01:00
s . CreateApp ( "PointOfSale" ) ;
var editUrl = s . Driver . Url ;
2023-06-22 08:57:29 +02:00
s . Driver . FindElement ( By . CssSelector ( "label[for='DefaultView_Light']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . SendKeys ( "EUR" ) ;
2023-12-31 09:07:15 +01:00
Assert . False ( s . Driver . FindElement ( By . Id ( "EnableTips" ) ) . Selected ) ;
s . Driver . FindElement ( By . Id ( "EnableTips" ) ) . Click ( ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "EnableTips" ) ) . Selected ) ;
Thread . Sleep ( 250 ) ;
2023-06-22 08:57:29 +02:00
s . Driver . FindElement ( By . Id ( "CustomTipPercentages" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "CustomTipPercentages" ) ) . SendKeys ( "10,21" ) ;
2023-12-31 09:07:15 +01:00
Assert . False ( s . Driver . FindElement ( By . Id ( "ShowDiscount" ) ) . Selected ) ;
2024-03-14 11:11:54 +01:00
Assert . False ( s . Driver . FindElement ( By . Id ( "ShowItems" ) ) . Selected ) ;
2023-12-31 09:07:15 +01:00
s . Driver . FindElement ( By . Id ( "ShowDiscount" ) ) . Click ( ) ;
2023-06-22 08:57:29 +02:00
s . Driver . FindElement ( By . Id ( "SaveSettings" ) ) . Click ( ) ;
Assert . Contains ( "App updated" , s . FindAlertMessage ( ) . Text ) ;
2024-03-14 11:11:54 +01:00
// View
2023-06-22 08:57:29 +02:00
s . Driver . FindElement ( By . Id ( "ViewApp" ) ) . Click ( ) ;
var windows = s . Driver . WindowHandles ;
Assert . Equal ( 2 , windows . Count ) ;
s . Driver . SwitchTo ( ) . Window ( windows [ 1 ] ) ;
s . Driver . WaitForElement ( By . ClassName ( "keypad" ) ) ;
2023-12-31 09:07:15 +01:00
2023-06-22 08:57:29 +02:00
// basic checks
2024-01-25 13:00:33 +01:00
var keypadUrl = s . Driver . Url ;
s . Driver . FindElement ( By . Id ( "RecentTransactionsToggle" ) ) ;
2024-03-14 11:11:54 +01:00
s . Driver . ElementDoesNotExist ( By . Id ( "ItemsListToggle" ) ) ;
2023-06-22 08:57:29 +02:00
Assert . Contains ( "EUR" , s . Driver . FindElement ( By . Id ( "Currency" ) ) . Text ) ;
Assert . Contains ( "0,00" , s . Driver . FindElement ( By . Id ( "Amount" ) ) . Text ) ;
Assert . Equal ( "" , s . Driver . FindElement ( By . Id ( "Calculation" ) ) . Text ) ;
2023-11-02 20:03:34 +01:00
Assert . True ( s . Driver . FindElement ( By . Id ( "ModeTablist-amounts" ) ) . Selected ) ;
2023-06-22 08:57:29 +02:00
Assert . False ( s . Driver . FindElement ( By . Id ( "ModeTablist-discount" ) ) . Enabled ) ;
Assert . False ( s . Driver . FindElement ( By . Id ( "ModeTablist-tip" ) ) . Enabled ) ;
2023-12-31 09:07:15 +01:00
2023-06-22 08:57:29 +02:00
// Amount: 1234,56
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='1']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='2']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='3']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='4']" ) ) . Click ( ) ;
2023-11-02 20:03:34 +01:00
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='0']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='0']" ) ) . Click ( ) ;
Assert . Equal ( "1.234,00" , s . Driver . FindElement ( By . Id ( "Amount" ) ) . Text ) ;
Assert . Equal ( "" , s . Driver . FindElement ( By . Id ( "Calculation" ) ) . Text ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='+']" ) ) . Click ( ) ;
2023-06-22 08:57:29 +02:00
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='5']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='6']" ) ) . Click ( ) ;
Assert . Equal ( "1.234,56" , s . Driver . FindElement ( By . Id ( "Amount" ) ) . Text ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "ModeTablist-discount" ) ) . Enabled ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "ModeTablist-tip" ) ) . Enabled ) ;
2023-11-02 20:03:34 +01:00
Assert . Equal ( "1.234,00 € + 0,56 €" , s . Driver . FindElement ( By . Id ( "Calculation" ) ) . Text ) ;
2023-12-31 09:07:15 +01:00
2023-06-22 08:57:29 +02:00
// Discount: 10%
s . Driver . FindElement ( By . CssSelector ( "label[for='ModeTablist-discount']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='1']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='0']" ) ) . Click ( ) ;
Assert . Contains ( "1.111,10" , s . Driver . FindElement ( By . Id ( "Amount" ) ) . Text ) ;
Assert . Contains ( "10% discount" , s . Driver . FindElement ( By . Id ( "Discount" ) ) . Text ) ;
2023-11-02 20:03:34 +01:00
Assert . Contains ( "1.234,00 € + 0,56 € - 123,46 € (10%)" , s . Driver . FindElement ( By . Id ( "Calculation" ) ) . Text ) ;
2023-12-31 09:07:15 +01:00
2023-06-22 08:57:29 +02:00
// Tip: 10%
s . Driver . FindElement ( By . CssSelector ( "label[for='ModeTablist-tip']" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "Tip-Custom" ) ) ;
s . Driver . FindElement ( By . Id ( "Tip-10" ) ) . Click ( ) ;
Assert . Contains ( "1.222,21" , s . Driver . FindElement ( By . Id ( "Amount" ) ) . Text ) ;
2023-11-02 20:03:34 +01:00
Assert . Contains ( "1.234,00 € + 0,56 € - 123,46 € (10%) + 111,11 € (10%)" , s . Driver . FindElement ( By . Id ( "Calculation" ) ) . Text ) ;
2023-12-31 09:07:15 +01:00
2023-06-22 08:57:29 +02:00
// Pay
s . Driver . FindElement ( By . Id ( "pay-button" ) ) . Click ( ) ;
s . Driver . WaitUntilAvailable ( By . Id ( "Checkout-v2" ) ) ;
s . Driver . FindElement ( By . Id ( "DetailsToggle" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "PaymentDetails-TotalFiat" ) ) ;
Assert . Contains ( "1 222,21 €" , s . Driver . FindElement ( By . Id ( "PaymentDetails-TotalFiat" ) ) . Text ) ;
2024-03-14 11:11:54 +01:00
s . PayInvoice ( true ) ;
TestUtils . Eventually ( ( ) = >
{
s . MineBlockOnInvoiceCheckout ( ) ;
Assert . True ( s . Driver . WaitForElement ( By . Id ( "settled" ) ) . Displayed ) ;
} ) ;
// Receipt
s . Driver . WaitForElement ( By . Id ( "ReceiptLink" ) ) . Click ( ) ;
2024-04-24 10:22:00 +02:00
var cartData = s . Driver . FindElement ( By . CssSelector ( "#CartData table" ) ) ;
var items = cartData . FindElements ( By . CssSelector ( "tbody tr" ) ) ;
var sums = cartData . FindElements ( By . CssSelector ( "tfoot tr" ) ) ;
2024-03-14 11:11:54 +01:00
Assert . Equal ( 2 , items . Count ) ;
Assert . Equal ( 4 , sums . Count ) ;
Assert . Contains ( "Manual entry 1" , items [ 0 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "1 234,00 €" , items [ 0 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Manual entry 2" , items [ 1 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "0,56 €" , items [ 1 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Subtotal" , sums [ 0 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "1 234,56 €" , sums [ 0 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Discount" , sums [ 1 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "10% = 123,46 €" , sums [ 1 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Tip" , sums [ 2 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "10% = 111,11 €" , sums [ 2 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Total" , sums [ 3 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "1 222,21 €" , sums [ 3 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
2024-06-11 16:04:25 +02:00
// Receipt print
s . Driver . FindElement ( By . Id ( "ReceiptLinkPrint" ) ) . Click ( ) ;
windows = s . Driver . WindowHandles ;
Assert . Equal ( 3 , windows . Count ) ;
s . Driver . SwitchTo ( ) . Window ( windows [ 2 ] ) ;
var paymentDetails = s . Driver . WaitForElement ( By . CssSelector ( "#PaymentDetails table" ) ) ;
items = paymentDetails . FindElements ( By . CssSelector ( "tr.cart-data" ) ) ;
sums = paymentDetails . FindElements ( By . CssSelector ( "tr.sums-data" ) ) ;
Assert . Equal ( 2 , items . Count ) ;
Assert . Equal ( 4 , sums . Count ) ;
Assert . Contains ( "Manual entry 1" , items [ 0 ] . FindElement ( By . CssSelector ( ".key" ) ) . Text ) ;
Assert . Contains ( "1 234,00 €" , items [ 0 ] . FindElement ( By . CssSelector ( ".val" ) ) . Text ) ;
Assert . Contains ( "Manual entry 2" , items [ 1 ] . FindElement ( By . CssSelector ( ".key" ) ) . Text ) ;
Assert . Contains ( "0,56 €" , items [ 1 ] . FindElement ( By . CssSelector ( ".val" ) ) . Text ) ;
Assert . Contains ( "Subtotal" , sums [ 0 ] . FindElement ( By . CssSelector ( ".key" ) ) . Text ) ;
Assert . Contains ( "1 234,56 €" , sums [ 0 ] . FindElement ( By . CssSelector ( ".val" ) ) . Text ) ;
Assert . Contains ( "Discount" , sums [ 1 ] . FindElement ( By . CssSelector ( ".key" ) ) . Text ) ;
Assert . Contains ( "10% = 123,46 €" , sums [ 1 ] . FindElement ( By . CssSelector ( ".val" ) ) . Text ) ;
Assert . Contains ( "Tip" , sums [ 2 ] . FindElement ( By . CssSelector ( ".key" ) ) . Text ) ;
Assert . Contains ( "10% = 111,11 €" , sums [ 2 ] . FindElement ( By . CssSelector ( ".val" ) ) . Text ) ;
Assert . Contains ( "Total" , sums [ 3 ] . FindElement ( By . CssSelector ( ".key" ) ) . Text ) ;
Assert . Contains ( "1 222,21 €" , sums [ 3 ] . FindElement ( By . CssSelector ( ".val" ) ) . Text ) ;
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( windows [ 1 ] ) ;
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2024-03-14 11:11:54 +01:00
// Once more with items
s . GoToUrl ( editUrl ) ;
s . Driver . FindElement ( By . Id ( "ShowItems" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "SaveSettings" ) ) . Click ( ) ;
Assert . Contains ( "App updated" , s . FindAlertMessage ( ) . Text ) ;
s . GoToUrl ( keypadUrl ) ;
s . Driver . WaitForElement ( By . ClassName ( "keypad" ) ) ;
s . Driver . FindElement ( By . Id ( "ItemsListToggle" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
Assert . True ( s . Driver . WaitForElement ( By . Id ( "PosItems" ) ) . Displayed ) ;
s . Driver . FindElement ( By . CssSelector ( "#PosItems .posItem--displayed:nth-child(1) .btn-plus" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( "#PosItems .posItem--displayed:nth-child(1) .btn-plus" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( "#PosItems .posItem--displayed:nth-child(2) .btn-plus" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( "#ItemsListOffcanvas button[data-bs-dismiss=\"offcanvas\"]" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='1']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='2']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".keypad [data-key='3']" ) ) . Click ( ) ;
Assert . Contains ( "4,23" , s . Driver . FindElement ( By . Id ( "Amount" ) ) . Text ) ;
Assert . Contains ( "2 x Green Tea (1,00 €) = 2,00 € + 1 x Black Tea (1,00 €) = 1,00 € + 1,23 €" , s . Driver . FindElement ( By . Id ( "Calculation" ) ) . Text ) ;
// Pay
s . Driver . FindElement ( By . Id ( "pay-button" ) ) . Click ( ) ;
s . Driver . WaitUntilAvailable ( By . Id ( "Checkout-v2" ) ) ;
s . Driver . FindElement ( By . Id ( "DetailsToggle" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "PaymentDetails-TotalFiat" ) ) ;
Assert . Contains ( "4,23 €" , s . Driver . FindElement ( By . Id ( "PaymentDetails-TotalFiat" ) ) . Text ) ;
s . PayInvoice ( true ) ;
TestUtils . Eventually ( ( ) = >
{
s . MineBlockOnInvoiceCheckout ( ) ;
Assert . True ( s . Driver . WaitForElement ( By . Id ( "settled" ) ) . Displayed ) ;
} ) ;
// Receipt
s . Driver . WaitForElement ( By . Id ( "ReceiptLink" ) ) . Click ( ) ;
2024-04-24 10:22:00 +02:00
cartData = s . Driver . FindElement ( By . CssSelector ( "#CartData table" ) ) ;
items = cartData . FindElements ( By . CssSelector ( "tbody tr" ) ) ;
sums = cartData . FindElements ( By . CssSelector ( "tfoot tr" ) ) ;
2024-03-14 11:11:54 +01:00
Assert . Equal ( 3 , items . Count ) ;
2024-04-24 10:22:00 +02:00
Assert . Single ( sums ) ;
2024-03-14 11:11:54 +01:00
Assert . Contains ( "Black Tea" , items [ 0 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "1 x 1,00 € = 1,00 €" , items [ 0 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Green Tea" , items [ 1 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "2 x 1,00 € = 2,00 €" , items [ 1 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Manual entry 1" , items [ 2 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "1,23 €" , items [ 2 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
2024-04-24 10:22:00 +02:00
Assert . Contains ( "Total" , sums [ 0 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
2024-03-14 11:11:54 +01:00
Assert . Contains ( "4,23 €" , sums [ 0 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
2024-01-25 13:00:33 +01:00
2024-06-11 16:04:25 +02:00
// Receipt print
s . Driver . FindElement ( By . Id ( "ReceiptLinkPrint" ) ) . Click ( ) ;
windows = s . Driver . WindowHandles ;
Assert . Equal ( 2 , windows . Count ) ;
s . Driver . SwitchTo ( ) . Window ( windows [ 1 ] ) ;
paymentDetails = s . Driver . WaitForElement ( By . CssSelector ( "#PaymentDetails table" ) ) ;
items = paymentDetails . FindElements ( By . CssSelector ( "tr.cart-data" ) ) ;
sums = paymentDetails . FindElements ( By . CssSelector ( "tr.sums-data" ) ) ;
Assert . Equal ( 3 , items . Count ) ;
Assert . Single ( sums ) ;
Assert . Contains ( "Black Tea" , items [ 0 ] . FindElement ( By . CssSelector ( ".key" ) ) . Text ) ;
Assert . Contains ( "1 x 1,00 € = 1,00 €" , items [ 0 ] . FindElement ( By . CssSelector ( ".val" ) ) . Text ) ;
Assert . Contains ( "Green Tea" , items [ 1 ] . FindElement ( By . CssSelector ( ".key" ) ) . Text ) ;
Assert . Contains ( "2 x 1,00 € = 2,00 €" , items [ 1 ] . FindElement ( By . CssSelector ( ".val" ) ) . Text ) ;
Assert . Contains ( "Manual entry 1" , items [ 2 ] . FindElement ( By . CssSelector ( ".key" ) ) . Text ) ;
Assert . Contains ( "1,23 €" , items [ 2 ] . FindElement ( By . CssSelector ( ".val" ) ) . Text ) ;
Assert . Contains ( "Total" , sums [ 0 ] . FindElement ( By . CssSelector ( ".key" ) ) . Text ) ;
Assert . Contains ( "4,23 €" , sums [ 0 ] . FindElement ( By . CssSelector ( ".val" ) ) . Text ) ;
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2024-01-25 13:00:33 +01:00
// Guest user can access recent transactions
s . GoToHome ( ) ;
s . Logout ( ) ;
s . LogIn ( user , userAccount . RegisterDetails . Password ) ;
s . GoToUrl ( keypadUrl ) ;
s . Driver . FindElement ( By . Id ( "RecentTransactionsToggle" ) ) ;
s . GoToHome ( ) ;
s . Logout ( ) ;
// Unauthenticated user can't access recent transactions
s . GoToUrl ( keypadUrl ) ;
s . Driver . ElementDoesNotExist ( By . Id ( "RecentTransactionsToggle" ) ) ;
2023-06-22 08:57:29 +02:00
}
2023-07-05 10:23:15 +02:00
[Fact]
[Trait("Selenium", "Selenium")]
public async Task CanUsePOSCart ( )
{
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
2024-03-14 11:11:54 +01:00
// Create users
var user = s . RegisterNewUser ( ) ;
var userAccount = s . AsTestAccount ( ) ;
s . GoToHome ( ) ;
s . Logout ( ) ;
s . GoToRegister ( ) ;
2023-07-05 10:23:15 +02:00
s . RegisterNewUser ( true ) ;
2024-03-14 11:11:54 +01:00
// Setup store and associate user
( _ , string storeId ) = s . CreateNewStore ( ) ;
2023-07-05 10:23:15 +02:00
s . GoToStore ( ) ;
2024-03-14 11:11:54 +01:00
s . AddDerivationScheme ( ) ;
s . AddUserToStore ( storeId , user , "Guest" ) ;
// Setup POS
s . CreateApp ( "PointOfSale" ) ;
2023-07-05 10:23:15 +02:00
s . Driver . FindElement ( By . CssSelector ( "label[for='DefaultView_Cart']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Currency" ) ) . SendKeys ( "EUR" ) ;
2023-12-31 09:07:15 +01:00
Assert . False ( s . Driver . FindElement ( By . Id ( "EnableTips" ) ) . Selected ) ;
s . Driver . FindElement ( By . Id ( "EnableTips" ) ) . Click ( ) ;
Assert . True ( s . Driver . FindElement ( By . Id ( "EnableTips" ) ) . Selected ) ;
Thread . Sleep ( 250 ) ;
2023-07-22 14:15:41 +02:00
s . Driver . FindElement ( By . Id ( "CustomTipPercentages" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "CustomTipPercentages" ) ) . SendKeys ( "10,21" ) ;
2023-12-31 09:07:15 +01:00
Assert . False ( s . Driver . FindElement ( By . Id ( "ShowDiscount" ) ) . Selected ) ;
s . Driver . FindElement ( By . Id ( "ShowDiscount" ) ) . Click ( ) ;
2023-07-05 10:23:15 +02:00
s . Driver . FindElement ( By . Id ( "SaveSettings" ) ) . Click ( ) ;
Assert . Contains ( "App updated" , s . FindAlertMessage ( ) . Text ) ;
2024-03-14 11:11:54 +01:00
// View
2023-07-05 10:23:15 +02:00
s . Driver . FindElement ( By . Id ( "ViewApp" ) ) . Click ( ) ;
var windows = s . Driver . WindowHandles ;
Assert . Equal ( 2 , windows . Count ) ;
s . Driver . SwitchTo ( ) . Window ( windows [ 1 ] ) ;
2023-07-22 14:15:41 +02:00
s . Driver . WaitForElement ( By . Id ( "PosItems" ) ) ;
Assert . Empty ( s . Driver . FindElements ( By . CssSelector ( "#CartItems tr" ) ) ) ;
2023-08-09 09:31:19 +02:00
var posUrl = s . Driver . Url ;
2023-07-05 10:23:15 +02:00
// Select and clear
2023-07-22 14:15:41 +02:00
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(1) .btn-primary" ) ) . Click ( ) ;
Assert . Single ( s . Driver . FindElements ( By . CssSelector ( "#CartItems tr" ) ) ) ;
2023-07-05 10:23:15 +02:00
s . Driver . FindElement ( By . Id ( "CartClear" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
2023-07-22 14:15:41 +02:00
Assert . Empty ( s . Driver . FindElements ( By . CssSelector ( "#CartItems tr" ) ) ) ;
2023-07-05 10:23:15 +02:00
2023-08-09 09:31:19 +02:00
// Select simple items
2023-07-22 14:15:41 +02:00
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(1) .btn-primary" ) ) . Click ( ) ;
2023-07-05 10:23:15 +02:00
Thread . Sleep ( 250 ) ;
2023-08-09 09:31:19 +02:00
Assert . Single ( s . Driver . FindElements ( By . CssSelector ( "#CartItems tr" ) ) ) ;
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(2) .btn-primary" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
2023-07-22 14:15:41 +02:00
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(2) .btn-primary" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
Assert . Equal ( 2 , s . Driver . FindElements ( By . CssSelector ( "#CartItems tr" ) ) . Count ) ;
Assert . Equal ( "3,00 €" , s . Driver . FindElement ( By . Id ( "CartTotal" ) ) . Text ) ;
2023-08-09 09:31:19 +02:00
// Select item with inventory - two of it
Assert . Equal ( "5 left" , s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(3) .badge.inventory" ) ) . Text ) ;
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(3) .btn-primary" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(3) .btn-primary" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
Assert . Equal ( 3 , s . Driver . FindElements ( By . CssSelector ( "#CartItems tr" ) ) . Count ) ;
Assert . Equal ( "5,40 €" , s . Driver . FindElement ( By . Id ( "CartTotal" ) ) . Text ) ;
// Select items with minimum amount
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(5) .btn-primary" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
Assert . Equal ( 4 , s . Driver . FindElements ( By . CssSelector ( "#CartItems tr" ) ) . Count ) ;
Assert . Equal ( "7,20 €" , s . Driver . FindElement ( By . Id ( "CartTotal" ) ) . Text ) ;
// Select items with adjusted minimum amount
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(5) input[name='amount']" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(5) input[name='amount']" ) ) . SendKeys ( "2.3" ) ;
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(5) .btn-primary" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
Assert . Equal ( 5 , s . Driver . FindElements ( By . CssSelector ( "#CartItems tr" ) ) . Count ) ;
Assert . Equal ( "9,50 €" , s . Driver . FindElement ( By . Id ( "CartTotal" ) ) . Text ) ;
// Select items with custom amount
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(6) input[name='amount']" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(6) input[name='amount']" ) ) . SendKeys ( ".2" ) ;
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(6) .btn-primary" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
Assert . Equal ( 6 , s . Driver . FindElements ( By . CssSelector ( "#CartItems tr" ) ) . Count ) ;
Assert . Equal ( "9,70 €" , s . Driver . FindElement ( By . Id ( "CartTotal" ) ) . Text ) ;
// Select items with another custom amount
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(6) input[name='amount']" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(6) input[name='amount']" ) ) . SendKeys ( ".3" ) ;
s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(6) .btn-primary" ) ) . Click ( ) ;
Thread . Sleep ( 250 ) ;
Assert . Equal ( 7 , s . Driver . FindElements ( By . CssSelector ( "#CartItems tr" ) ) . Count ) ;
Assert . Equal ( "10,00 €" , s . Driver . FindElement ( By . Id ( "CartTotal" ) ) . Text ) ;
2023-12-31 09:07:15 +01:00
2023-07-22 14:15:41 +02:00
// Discount: 10%
s . Driver . ElementDoesNotExist ( By . Id ( "CartDiscount" ) ) ;
s . Driver . FindElement ( By . Id ( "Discount" ) ) . SendKeys ( "10" ) ;
2023-08-09 09:31:19 +02:00
Assert . Contains ( "10% = 1,00 €" , s . Driver . FindElement ( By . Id ( "CartDiscount" ) ) . Text ) ;
Assert . Equal ( "9,00 €" , s . Driver . FindElement ( By . Id ( "CartTotal" ) ) . Text ) ;
2023-12-31 09:07:15 +01:00
2023-07-22 14:15:41 +02:00
// Tip: 10%
s . Driver . ElementDoesNotExist ( By . Id ( "CartTip" ) ) ;
s . Driver . FindElement ( By . Id ( "Tip-10" ) ) . Click ( ) ;
2023-08-09 09:31:19 +02:00
Assert . Contains ( "10% = 0,90 €" , s . Driver . FindElement ( By . Id ( "CartTip" ) ) . Text ) ;
Assert . Equal ( "9,90 €" , s . Driver . FindElement ( By . Id ( "CartTotal" ) ) . Text ) ;
2023-12-31 09:07:15 +01:00
2023-08-09 09:31:19 +02:00
// Check values on checkout page
2023-07-22 14:15:41 +02:00
s . Driver . FindElement ( By . Id ( "CartSubmit" ) ) . Click ( ) ;
2023-07-05 10:23:15 +02:00
s . Driver . WaitUntilAvailable ( By . Id ( "Checkout-v2" ) ) ;
s . Driver . FindElement ( By . Id ( "DetailsToggle" ) ) . Click ( ) ;
s . Driver . WaitForElement ( By . Id ( "PaymentDetails-TotalFiat" ) ) ;
2023-08-09 09:31:19 +02:00
Assert . Contains ( "9,90 €" , s . Driver . FindElement ( By . Id ( "PaymentDetails-TotalFiat" ) ) . Text ) ;
// Pay
2024-03-14 11:11:54 +01:00
s . PayInvoice ( true ) ;
TestUtils . Eventually ( ( ) = >
{
s . MineBlockOnInvoiceCheckout ( ) ;
Assert . True ( s . Driver . WaitForElement ( By . Id ( "settled" ) ) . Displayed ) ;
} ) ;
// Receipt
s . Driver . WaitForElement ( By . Id ( "ReceiptLink" ) ) . Click ( ) ;
2024-04-24 10:22:00 +02:00
var cartData = s . Driver . FindElement ( By . CssSelector ( "#CartData table" ) ) ;
var items = cartData . FindElements ( By . CssSelector ( "tbody tr" ) ) ;
var sums = cartData . FindElements ( By . CssSelector ( "tfoot tr" ) ) ;
2024-03-14 11:11:54 +01:00
Assert . Equal ( 7 , items . Count ) ;
Assert . Equal ( 4 , sums . Count ) ;
Assert . Contains ( "Black Tea" , items [ 0 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "2 x 1,00 € = 2,00 €" , items [ 0 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Green Tea" , items [ 1 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "1 x 1,00 € = 1,00 €" , items [ 1 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Rooibos (limited)" , items [ 2 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "2 x 1,20 € = 2,40 €" , items [ 2 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Herbal Tea (minimum) (1,80 €)" , items [ 3 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "1 x 1,80 € = 1,80 €" , items [ 3 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Herbal Tea (minimum) (2,30 €)" , items [ 4 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "1 x 2,30 € = 2,30 €" , items [ 4 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Fruit Tea (any amount) (0,20 €)" , items [ 5 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "1 x 0,20 € = 0,20 €" , items [ 5 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Fruit Tea (any amount) (0,30 €)" , items [ 6 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "1 x 0,30 € = 0,30 €" , items [ 6 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Subtotal" , sums [ 0 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "10,00 €" , sums [ 0 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Discount" , sums [ 1 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "10% = 1,00 €" , sums [ 1 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Tip" , sums [ 2 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "10% = 0,90 €" , sums [ 2 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
Assert . Contains ( "Total" , sums [ 3 ] . FindElement ( By . CssSelector ( "th" ) ) . Text ) ;
Assert . Contains ( "9,90 €" , sums [ 3 ] . FindElement ( By . CssSelector ( "td" ) ) . Text ) ;
2023-08-09 09:31:19 +02:00
// Check inventory got updated and is now 3 instead of 5
s . Driver . Navigate ( ) . GoToUrl ( posUrl ) ;
2024-03-14 11:11:54 +01:00
Assert . Equal ( "3 left" , s . Driver . FindElement ( By . CssSelector ( ".posItem:nth-child(3) .badge.inventory" ) ) . Text ) ;
// Guest user can access recent transactions
s . GoToHome ( ) ;
s . Logout ( ) ;
s . LogIn ( user , userAccount . RegisterDetails . Password ) ;
s . GoToUrl ( posUrl ) ;
s . Driver . FindElement ( By . Id ( "RecentTransactionsToggle" ) ) ;
s . GoToHome ( ) ;
s . Logout ( ) ;
// Unauthenticated user can't access recent transactions
s . GoToUrl ( posUrl ) ;
s . Driver . ElementDoesNotExist ( By . Id ( "RecentTransactionsToggle" ) ) ;
2023-07-05 10:23:15 +02:00
}
2023-06-22 08:57:29 +02:00
[Fact]
[Trait("Selenium", "Selenium")]
2021-10-26 13:55:13 +02:00
[Trait("Lightning", "Lightning")]
public async Task CanUseLNURL ( )
{
2021-11-22 09:16:08 +01:00
using var s = CreateSeleniumTester ( ) ;
2021-10-26 13:55:13 +02:00
s . Server . ActivateLightning ( ) ;
await s . StartAsync ( ) ;
await s . Server . EnsureChannelsSetup ( ) ;
2021-10-29 08:25:43 +02:00
var cryptoCode = "BTC" ;
await Lightning . Tests . ConnectChannels . ConnectAll ( s . Server . ExplorerNode ,
2021-10-26 13:55:13 +02:00
new [ ] { s . Server . MerchantLightningD } ,
new [ ] { s . Server . MerchantLnd . Client } ) ;
s . RegisterNewUser ( true ) ;
2021-12-31 08:36:38 +01:00
( _ , string storeId ) = s . CreateNewStore ( ) ;
2023-04-05 01:35:50 +02:00
s . EnableCheckout ( CheckoutType . V1 ) ;
2021-10-29 08:25:43 +02:00
var network = s . Server . NetworkProvider . GetNetwork < BTCPayNetwork > ( cryptoCode ) . NBitcoinNetwork ;
2021-12-31 14:02:53 +01:00
s . AddLightningNode ( LightningConnectionType . CLightning , false ) ;
s . GoToLightningSettings ( ) ;
2022-01-25 04:17:39 +01:00
// LNURL is true by default
Assert . True ( s . Driver . FindElement ( By . Id ( "LNURLEnabled" ) ) . Selected ) ;
2022-02-21 05:21:33 +01:00
s . Driver . SetCheckbox ( By . Name ( "LUD12Enabled" ) , true ) ;
s . Driver . FindElement ( By . Id ( "save" ) ) . Click ( ) ;
2021-10-29 08:25:43 +02:00
// Topup Invoice test
2021-12-31 08:36:38 +01:00
var i = s . CreateInvoice ( storeId , null , cryptoCode ) ;
2021-10-26 13:55:13 +02:00
s . GoToInvoiceCheckout ( i ) ;
2024-02-22 01:38:06 +01:00
s . Driver . WaitForElement ( By . Id ( "copy-tab" ) ) . Click ( ) ;
2021-10-26 13:55:13 +02:00
var lnurl = s . Driver . FindElement ( By . CssSelector ( "input.checkoutTextbox" ) ) . GetAttribute ( "value" ) ;
var parsed = LNURL . LNURL . Parse ( lnurl , out var tag ) ;
2021-10-29 11:01:16 +02:00
var fetchedReuqest =
Assert . IsType < LNURL . LNURLPayRequest > ( await LNURL . LNURL . FetchInformation ( parsed , new HttpClient ( ) ) ) ;
2021-10-26 13:55:13 +02:00
Assert . Equal ( 1 m , fetchedReuqest . MinSendable . ToDecimal ( LightMoneyUnit . Satoshi ) ) ;
Assert . NotEqual ( 1 m , fetchedReuqest . MaxSendable . ToDecimal ( LightMoneyUnit . Satoshi ) ) ;
var lnurlResponse = await fetchedReuqest . SendRequest ( new LightMoney ( 0.000001 m , LightMoneyUnit . BTC ) ,
2022-02-21 05:21:33 +01:00
network , new HttpClient ( ) , comment : "lol" ) ;
2021-10-29 11:01:16 +02:00
Assert . Equal ( new LightMoney ( 0.000001 m , LightMoneyUnit . BTC ) ,
lnurlResponse . GetPaymentRequest ( network ) . MinimumAmount ) ;
2021-10-26 13:55:13 +02:00
var lnurlResponse2 = await fetchedReuqest . SendRequest ( new LightMoney ( 0.000002 m , LightMoneyUnit . BTC ) ,
2022-02-21 05:21:33 +01:00
network , new HttpClient ( ) , comment : "lol2" ) ;
2021-10-26 13:55:13 +02:00
Assert . Equal ( new LightMoney ( 0.000002 m , LightMoneyUnit . BTC ) , lnurlResponse2 . GetPaymentRequest ( network ) . MinimumAmount ) ;
2022-05-18 07:57:36 +02:00
// Initial bolt was cancelled
var res = await s . Server . CustomerLightningD . Pay ( lnurlResponse . Pr ) ;
Assert . Equal ( PayResult . Error , res . Result ) ;
2021-12-31 08:59:02 +01:00
2023-04-10 06:30:38 +02:00
res = await s . Server . CustomerLightningD . Pay ( lnurlResponse2 . Pr ) ;
Assert . Equal ( PayResult . Ok , res . Result ) ;
2021-10-26 13:55:13 +02:00
await TestUtils . EventuallyAsync ( async ( ) = >
{
var inv = await s . Server . PayTester . InvoiceRepository . GetInvoice ( i ) ;
Assert . Equal ( InvoiceStatusLegacy . Complete , inv . Status ) ;
} ) ;
2022-02-21 05:21:33 +01:00
var greenfield = await s . AsTestAccount ( ) . CreateClient ( ) ;
var paymentMethods = await greenfield . GetInvoicePaymentMethods ( s . StoreId , i ) ;
2023-01-06 14:18:07 +01:00
Assert . Single ( paymentMethods , p = >
{
2022-02-21 05:21:33 +01:00
return p . AdditionalData [ "providedComment" ] . Value < string > ( ) = = "lol2" ;
} ) ;
2021-10-29 08:25:43 +02:00
// Standard invoice test
2021-12-31 08:36:38 +01:00
s . GoToStore ( storeId ) ;
i = s . CreateInvoice ( storeId , 0.0000001 m , cryptoCode ) ;
2021-10-26 13:55:13 +02:00
s . GoToInvoiceCheckout ( i ) ;
2023-04-24 12:26:56 +02:00
s . Driver . FindElement ( By . ClassName ( "payment__currencies_noborder" ) ) . Click ( ) ;
// BOLT11 is also displayed for standard invoice (not LNURL, even if it is available)
2024-02-22 01:38:06 +01:00
s . Driver . WaitForElement ( By . Id ( "copy-tab" ) ) . Click ( ) ;
2023-04-24 12:26:56 +02:00
var bolt11 = s . Driver . FindElement ( By . CssSelector ( "input.checkoutTextbox" ) ) . GetAttribute ( "value" ) ;
2024-01-18 01:47:39 +01:00
Lightning . BOLT11PaymentRequest . Parse ( bolt11 , s . Server . ExplorerNode . Network ) ;
2023-04-24 12:26:56 +02:00
var invoiceId = s . Driver . Url . Split ( '/' ) . Last ( ) ;
using ( var resp = await s . Server . PayTester . HttpClient . GetAsync ( "BTC/lnurl/pay/i/" + invoiceId ) )
{
resp . EnsureSuccessStatusCode ( ) ;
fetchedReuqest = JsonConvert . DeserializeObject < LNURLPayRequest > ( await resp . Content . ReadAsStringAsync ( ) ) ;
}
2021-10-26 13:55:13 +02:00
Assert . Equal ( 0.0000001 m , fetchedReuqest . MaxSendable . ToDecimal ( LightMoneyUnit . BTC ) ) ;
Assert . Equal ( 0.0000001 m , fetchedReuqest . MinSendable . ToDecimal ( LightMoneyUnit . BTC ) ) ;
2023-04-24 12:26:56 +02:00
2023-01-05 14:41:18 +01:00
await Assert . ThrowsAsync < LNUrlException > ( async ( ) = >
2021-10-26 13:55:13 +02:00
{
await fetchedReuqest . SendRequest ( new LightMoney ( 0.0000002 m , LightMoneyUnit . BTC ) ,
network , new HttpClient ( ) ) ;
} ) ;
2023-01-05 14:41:18 +01:00
await Assert . ThrowsAsync < LNUrlException > ( async ( ) = >
2021-10-26 13:55:13 +02:00
{
await fetchedReuqest . SendRequest ( new LightMoney ( 0.00000005 m , LightMoneyUnit . BTC ) ,
network , new HttpClient ( ) ) ;
} ) ;
2021-10-29 11:01:16 +02:00
2021-10-26 13:55:13 +02:00
lnurlResponse = await fetchedReuqest . SendRequest ( new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ,
network , new HttpClient ( ) ) ;
lnurlResponse2 = await fetchedReuqest . SendRequest ( new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ,
network , new HttpClient ( ) ) ;
2021-10-29 11:01:16 +02:00
//invoice amounts do no change so the paymnet request is not regenerated
Assert . Equal ( lnurlResponse . Pr , lnurlResponse2 . Pr ) ;
2021-10-26 13:55:13 +02:00
await s . Server . CustomerLightningD . Pay ( lnurlResponse . Pr ) ;
2021-10-29 11:01:16 +02:00
Assert . Equal ( new LightMoney ( 0.0000001 m , LightMoneyUnit . BTC ) ,
lnurlResponse2 . GetPaymentRequest ( network ) . MinimumAmount ) ;
2021-12-31 14:02:53 +01:00
s . GoToHome ( ) ;
2021-12-31 08:59:02 +01:00
2021-12-31 08:36:38 +01:00
i = s . CreateInvoice ( storeId , 0.000001 m , cryptoCode ) ;
2021-10-26 13:55:13 +02:00
s . GoToInvoiceCheckout ( i ) ;
s . Driver . FindElement ( By . ClassName ( "payment__currencies_noborder" ) ) ;
2021-12-31 08:36:38 +01:00
s . GoToStore ( storeId ) ;
i = s . CreateInvoice ( storeId , null , cryptoCode ) ;
2021-10-26 13:55:13 +02:00
s . GoToInvoiceCheckout ( i ) ;
s . Driver . FindElement ( By . ClassName ( "payment__currencies_noborder" ) ) ;
2021-10-29 11:01:16 +02:00
2021-12-31 14:02:53 +01:00
s . GoToHome ( ) ;
s . GoToLightningSettings ( ) ;
2021-10-29 08:25:43 +02:00
s . Driver . SetCheckbox ( By . Id ( "LNURLBech32Mode" ) , false ) ;
s . Driver . FindElement ( By . Id ( "save" ) ) . Click ( ) ;
Assert . Contains ( $"{cryptoCode} Lightning settings successfully updated" , s . FindAlertMessage ( ) . Text ) ;
2021-12-31 08:59:02 +01:00
2021-10-29 08:25:43 +02:00
// Ensure the toggles are set correctly
2021-12-31 14:02:53 +01:00
s . GoToLightningSettings ( ) ;
2021-10-29 08:25:43 +02:00
Assert . False ( s . Driver . FindElement ( By . Id ( "LNURLBech32Mode" ) ) . Selected ) ;
2021-12-31 08:59:02 +01:00
2021-12-31 08:36:38 +01:00
i = s . CreateInvoice ( storeId , null , cryptoCode ) ;
2021-10-26 13:55:13 +02:00
s . GoToInvoiceCheckout ( i ) ;
s . Driver . FindElement ( By . ClassName ( "payment__currencies_noborder" ) ) ;
2024-02-22 01:38:06 +01:00
s . Driver . WaitForElement ( By . Id ( "copy-tab" ) ) . Click ( ) ;
2021-10-26 13:55:13 +02:00
lnurl = s . Driver . FindElement ( By . CssSelector ( "input.checkoutTextbox" ) ) . GetAttribute ( "value" ) ;
Assert . StartsWith ( "lnurlp" , lnurl ) ;
2021-10-29 08:25:43 +02:00
LNURL . LNURL . Parse ( lnurl , out tag ) ;
2021-12-31 08:59:02 +01:00
2021-10-26 13:55:13 +02:00
s . GoToHome ( ) ;
2021-12-31 14:02:53 +01:00
s . CreateNewStore ( false ) ;
2023-04-05 01:35:50 +02:00
s . EnableCheckout ( CheckoutType . V1 ) ;
2021-12-31 14:02:53 +01:00
s . AddLightningNode ( LightningConnectionType . LndREST , false ) ;
s . GoToLightningSettings ( ) ;
2021-10-29 08:25:43 +02:00
s . Driver . SetCheckbox ( By . Id ( "LNURLEnabled" ) , true ) ;
s . Driver . FindElement ( By . Id ( "save" ) ) . Click ( ) ;
Assert . Contains ( $"{cryptoCode} Lightning settings successfully updated" , s . FindAlertMessage ( ) . Text ) ;
2023-04-25 12:21:27 +02:00
var invForPP = s . CreateInvoice ( null , cryptoCode ) ;
2021-10-26 13:55:13 +02:00
s . GoToInvoiceCheckout ( invForPP ) ;
2024-02-22 01:38:06 +01:00
s . Driver . WaitForElement ( By . Id ( "copy-tab" ) ) . Click ( ) ;
2021-10-26 13:55:13 +02:00
lnurl = s . Driver . FindElement ( By . CssSelector ( "input.checkoutTextbox" ) ) . GetAttribute ( "value" ) ;
2023-04-25 12:21:27 +02:00
LNURL . LNURL . Parse ( lnurl , out tag ) ;
2021-12-31 08:59:02 +01:00
2021-10-29 08:25:43 +02:00
// Check that pull payment has lightning option
2021-10-26 13:55:13 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
s . Driver . FindElement ( By . Id ( "NewPullPayment" ) ) . Click ( ) ;
2021-12-31 08:59:02 +01:00
Assert . Equal ( new PaymentMethodId ( cryptoCode , PaymentTypes . LightningLike ) , PaymentMethodId . Parse ( Assert . Single ( s . Driver . FindElements ( By . CssSelector ( "input[name='PaymentMethods']" ) ) ) . GetAttribute ( "value" ) ) ) ;
2021-10-26 13:55:13 +02:00
s . Driver . FindElement ( By . Id ( "Name" ) ) . SendKeys ( "PP1" ) ;
s . Driver . FindElement ( By . Id ( "Amount" ) ) . Clear ( ) ;
2021-10-29 11:01:16 +02:00
s . Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( "0.0000001" ) ;
2023-01-06 14:18:07 +01:00
2022-04-11 10:50:30 +02:00
var currencyInput = s . Driver . FindElement ( By . Id ( "Currency" ) ) ;
Assert . Equal ( "USD" , currencyInput . GetAttribute ( "value" ) ) ;
currencyInput . Clear ( ) ;
currencyInput . SendKeys ( "BTC" ) ;
2023-01-06 14:18:07 +01:00
2021-10-26 13:55:13 +02:00
s . Driver . FindElement ( By . Id ( "Create" ) ) . Click ( ) ;
s . Driver . FindElement ( By . LinkText ( "View" ) ) . Click ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . Last ( ) ) ;
2021-10-26 13:55:13 +02:00
var pullPaymentId = s . Driver . Url . Split ( '/' ) . Last ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . FindElement ( By . Id ( "Destination" ) ) . SendKeys ( lnurl ) ;
2021-10-26 13:55:13 +02:00
s . Driver . FindElement ( By . Id ( "ClaimedAmount" ) ) . Clear ( ) ;
s . Driver . FindElement ( By . Id ( "ClaimedAmount" ) ) . SendKeys ( "0.0000001" + Keys . Enter ) ;
s . FindAlertMessage ( ) ;
2023-11-20 02:45:43 +01:00
s . Driver . Close ( ) ;
s . Driver . SwitchTo ( ) . Window ( s . Driver . WindowHandles . First ( ) ) ;
2021-10-26 13:55:13 +02:00
s . GoToStore ( s . StoreId , StoreNavPages . PullPayments ) ;
var payouts = s . Driver . FindElements ( By . ClassName ( "pp-payout" ) ) ;
payouts [ 0 ] . Click ( ) ;
2021-10-29 11:01:16 +02:00
s . Driver . FindElement ( By . Id ( "BTC_LightningLike-view" ) ) . Click ( ) ;
2021-10-26 13:55:13 +02:00
Assert . NotEmpty ( s . Driver . FindElements ( By . ClassName ( "payout" ) ) ) ;
2023-11-02 08:12:28 +01:00
s . Driver . FindElement ( By . ClassName ( "mass-action-select-all" ) ) . Click ( ) ;
2021-10-26 13:55:13 +02:00
s . Driver . FindElement ( By . Id ( $"{PayoutState.AwaitingApproval}-approve-pay" ) ) . Click ( ) ;
Assert . Contains ( lnurl , s . Driver . PageSource ) ;
2021-10-29 11:01:16 +02:00
2021-10-26 13:55:13 +02:00
s . Driver . FindElement ( By . Id ( "pay-invoices-form" ) ) . Submit ( ) ;
2021-10-29 11:01:16 +02:00
2021-10-26 13:55:13 +02:00
await TestUtils . EventuallyAsync ( async ( ) = >
{
var inv = await s . Server . PayTester . InvoiceRepository . GetInvoice ( invForPP ) ;
Assert . Equal ( InvoiceStatusLegacy . Complete , inv . Status ) ;
await 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 = = PayoutState . Completed ) ) ;
} ) ;
2020-06-24 03:34:09 +02:00
}
2021-01-22 17:49:26 +01:00
2021-10-29 11:01:16 +02:00
[Fact]
[Trait("Selenium", "Selenium")]
[Trait("Lightning", "Lightning")]
public async Task CanUseLNAddress ( )
{
2021-11-22 09:16:08 +01:00
using var s = CreateSeleniumTester ( ) ;
2021-10-29 11:01:16 +02:00
s . Server . ActivateLightning ( ) ;
await s . StartAsync ( ) ;
await s . Server . EnsureChannelsSetup ( ) ;
s . RegisterNewUser ( true ) ;
//ln address tests
2021-11-11 06:30:19 +01:00
s . CreateNewStore ( ) ;
2021-11-10 10:50:39 +01:00
//ensure ln address is not available as Lightning is not enable
2021-12-31 08:36:38 +01:00
s . Driver . AssertElementNotFound ( By . Id ( "StoreNav-LightningAddress" ) ) ;
2021-10-29 11:01:16 +02:00
2021-12-31 14:02:53 +01:00
s . AddLightningNode ( LightningConnectionType . LndREST , false ) ;
2021-12-31 08:59:02 +01:00
2021-12-31 08:36:38 +01:00
s . Driver . FindElement ( By . Id ( "StoreNav-LightningAddress" ) ) . Click ( ) ;
2021-10-29 11:01:16 +02:00
s . Driver . ToggleCollapse ( "AddAddress" ) ;
var lnaddress1 = Guid . NewGuid ( ) . ToString ( ) ;
s . Driver . FindElement ( By . Id ( "Add_Username" ) ) . SendKeys ( lnaddress1 ) ;
s . Driver . FindElement ( By . CssSelector ( "button[value='add']" ) ) . Click ( ) ;
s . FindAlertMessage ( StatusMessageModel . StatusSeverity . Success ) ;
s . Driver . ToggleCollapse ( "AddAddress" ) ;
2023-04-07 10:48:58 +02:00
2021-10-29 11:01:16 +02:00
var lnaddress2 = "EUR" + Guid . NewGuid ( ) . ToString ( ) ;
s . Driver . FindElement ( By . Id ( "Add_Username" ) ) . SendKeys ( lnaddress2 ) ;
2023-04-07 10:48:58 +02:00
lnaddress2 = lnaddress2 . ToLowerInvariant ( ) ;
2021-10-29 11:01:16 +02:00
s . Driver . ToggleCollapse ( "AdvancedSettings" ) ;
s . Driver . FindElement ( By . Id ( "Add_CurrencyCode" ) ) . SendKeys ( "EUR" ) ;
s . Driver . FindElement ( By . Id ( "Add_Min" ) ) . SendKeys ( "2" ) ;
s . Driver . FindElement ( By . Id ( "Add_Max" ) ) . SendKeys ( "10" ) ;
2023-04-07 10:48:58 +02:00
s . Driver . FindElement ( By . Id ( "Add_InvoiceMetadata" ) ) . SendKeys ( "{\"test\":\"lol\"}" ) ;
2021-10-29 11:01:16 +02:00
s . Driver . FindElement ( By . CssSelector ( "button[value='add']" ) ) . Click ( ) ;
s . FindAlertMessage ( StatusMessageModel . StatusSeverity . Success ) ;
var addresses = s . Driver . FindElements ( By . ClassName ( "lightning-address-value" ) ) ;
Assert . Equal ( 2 , addresses . Count ) ;
2023-06-24 16:14:54 +02:00
var callbacks = new List < Uri > ( ) ;
2021-10-29 11:01:16 +02:00
foreach ( IWebElement webElement in addresses )
{
var value = webElement . GetAttribute ( "value" ) ;
//cannot test this directly as https is not supported on our e2e tests
// var request = await LNURL.LNURL.FetchPayRequestViaInternetIdentifier(value, new HttpClient());
var lnurl = new Uri ( LNURL . LNURL . ExtractUriFromInternetIdentifier ( value ) . ToString ( )
. Replace ( "https" , "http" ) ) ;
2021-12-31 08:59:02 +01:00
var request = ( LNURL . LNURLPayRequest ) await LNURL . LNURL . FetchInformation ( lnurl , new HttpClient ( ) ) ;
2023-04-07 10:48:58 +02:00
var m = request . ParsedMetadata . ToDictionary ( o = > o . Key , o = > o . Value ) ;
2021-10-29 11:01:16 +02:00
switch ( value )
{
case { } v when v . StartsWith ( lnaddress2 ) :
2023-04-07 10:48:58 +02:00
Assert . StartsWith ( lnaddress2 + "@" , m [ "text/identifier" ] ) ;
lnaddress2 = m [ "text/identifier" ] ;
2021-10-29 11:01:16 +02:00
Assert . Equal ( 2 , request . MinSendable . ToDecimal ( LightMoneyUnit . Satoshi ) ) ;
Assert . Equal ( 10 , request . MaxSendable . ToDecimal ( LightMoneyUnit . Satoshi ) ) ;
2023-06-24 16:14:54 +02:00
callbacks . Add ( request . Callback ) ;
2021-10-29 11:01:16 +02:00
break ;
case { } v when v . StartsWith ( lnaddress1 ) :
2023-04-07 10:48:58 +02:00
Assert . StartsWith ( lnaddress1 + "@" , m [ "text/identifier" ] ) ;
lnaddress1 = m [ "text/identifier" ] ;
2021-10-29 11:01:16 +02:00
Assert . Equal ( 1 , request . MinSendable . ToDecimal ( LightMoneyUnit . Satoshi ) ) ;
Assert . Equal ( 6.12 m , request . MaxSendable . ToDecimal ( LightMoneyUnit . BTC ) ) ;
2023-06-24 16:14:54 +02:00
callbacks . Add ( request . Callback ) ;
2021-10-29 11:01:16 +02:00
break ;
2023-04-07 10:48:58 +02:00
default :
2023-11-28 15:20:03 +01:00
Assert . Fail ( "Should have matched" ) ;
2023-04-07 10:48:58 +02:00
break ;
2021-10-29 11:01:16 +02:00
}
}
2023-04-07 10:48:58 +02:00
var repo = s . Server . PayTester . GetService < InvoiceRepository > ( ) ;
2023-06-24 16:14:54 +02:00
2023-04-07 10:48:58 +02:00
var invoices = await repo . GetInvoices ( new InvoiceQuery ( ) { StoreId = new [ ] { s . StoreId } } ) ;
2023-06-24 16:14:54 +02:00
// Resolving a ln address shouldn't create any btcpay invoice.
// This must be done because some NOST clients resolve ln addresses preemptively without user interaction
Assert . Empty ( invoices ) ;
// Calling the callbacks should create the invoices
foreach ( var callback in callbacks )
{
using var r = await s . Server . PayTester . HttpClient . GetAsync ( callback ) ;
await r . Content . ReadAsStringAsync ( ) ;
}
invoices = await repo . GetInvoices ( new InvoiceQuery ( ) { StoreId = new [ ] { s . StoreId } } ) ;
2023-04-07 10:48:58 +02:00
Assert . Equal ( 2 , invoices . Length ) ;
foreach ( var i in invoices )
{
var lightningPaymentMethod = i . GetPaymentMethod ( new PaymentMethodId ( "BTC" , PaymentTypes . LNURLPay ) ) ;
var paymentMethodDetails =
lightningPaymentMethod . GetPaymentMethodDetails ( ) as LNURLPayPaymentMethodDetails ;
Assert . Contains (
paymentMethodDetails . ConsumedLightningAddress ,
new [ ] { lnaddress1 , lnaddress2 } ) ;
if ( paymentMethodDetails . ConsumedLightningAddress = = lnaddress2 )
{
Assert . Equal ( "lol" , i . Metadata . AdditionalData [ "test" ] . Value < string > ( ) ) ;
}
}
var lnUsername = lnaddress1 . Split ( '@' ) [ 0 ] ;
2023-04-10 09:38:49 +02:00
2023-04-07 10:48:58 +02:00
LNURLPayRequest req ;
using ( var resp = await s . Server . PayTester . HttpClient . GetAsync ( $"/.well-known/lnurlp/{lnUsername}" ) )
{
var str = await resp . Content . ReadAsStringAsync ( ) ;
req = JsonConvert . DeserializeObject < LNURLPayRequest > ( str ) ;
Assert . Contains ( req . ParsedMetadata , m = > m . Key = = "text/identifier" & & m . Value = = lnaddress1 ) ;
Assert . Contains ( req . ParsedMetadata , m = > m . Key = = "text/plain" & & m . Value . StartsWith ( "Paid to" ) ) ;
Assert . NotNull ( req . Callback ) ;
Assert . Equal ( new LightMoney ( 1000 ) , req . MinSendable ) ;
Assert . Equal ( LightMoney . FromUnit ( 6.12 m , LightMoneyUnit . BTC ) , req . MaxSendable ) ;
}
lnUsername = lnaddress2 . Split ( '@' ) [ 0 ] ;
using ( var resp = await s . Server . PayTester . HttpClient . GetAsync ( $"/.well-known/lnurlp/{lnUsername}" ) )
{
var str = await resp . Content . ReadAsStringAsync ( ) ;
req = JsonConvert . DeserializeObject < LNURLPayRequest > ( str ) ;
Assert . Equal ( new LightMoney ( 2000 ) , req . MinSendable ) ;
Assert . Equal ( new LightMoney ( 10_000 ) , req . MaxSendable ) ;
}
// Check if we can get the same payrequest through the callback
using ( var resp = await s . Server . PayTester . HttpClient . GetAsync ( req . Callback ) )
{
var str = await resp . Content . ReadAsStringAsync ( ) ;
req = JsonConvert . DeserializeObject < LNURLPayRequest > ( str ) ;
Assert . Equal ( new LightMoney ( 2000 ) , req . MinSendable ) ;
Assert . Equal ( new LightMoney ( 10_000 ) , req . MaxSendable ) ;
}
// Can we ask for invoice? (Should fail, below minSpendable)
using ( var resp = await s . Server . PayTester . HttpClient . GetAsync ( req . Callback + "?amount=1999" ) )
{
var str = await resp . Content . ReadAsStringAsync ( ) ;
var err = JsonConvert . DeserializeObject < LNUrlStatusResponse > ( str ) ;
Assert . Equal ( "Amount is out of bounds." , err . Reason ) ;
}
// Can we ask for invoice?
using ( var resp = await s . Server . PayTester . HttpClient . GetAsync ( req . Callback + "?amount=2000" ) )
{
var str = await resp . Content . ReadAsStringAsync ( ) ;
var succ = JsonConvert . DeserializeObject < LNURLPayRequest . LNURLPayRequestCallbackResponse > ( str ) ;
Assert . NotNull ( succ . Pr ) ;
Assert . Equal ( new LightMoney ( 2000 ) , BOLT11PaymentRequest . Parse ( succ . Pr , Network . RegTest ) . MinimumAmount ) ;
}
// Can we change comment?
using ( var resp = await s . Server . PayTester . HttpClient . GetAsync ( req . Callback + "?amount=2001" ) )
{
var str = await resp . Content . ReadAsStringAsync ( ) ;
var succ = JsonConvert . DeserializeObject < LNURLPayRequest . LNURLPayRequestCallbackResponse > ( str ) ;
Assert . NotNull ( succ . Pr ) ;
Assert . Equal ( new LightMoney ( 2001 ) , BOLT11PaymentRequest . Parse ( succ . Pr , Network . RegTest ) . MinimumAmount ) ;
}
2021-10-29 11:01:16 +02:00
}
2021-12-24 09:27:00 +01:00
[Fact]
[Trait("Selenium", "Selenium")]
public async Task CanSigninWithLoginCode ( )
{
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
var user = s . RegisterNewUser ( ) ;
2023-07-19 15:21:16 +02:00
s . GoToHome ( ) ;
2021-12-24 09:27:00 +01:00
s . GoToProfile ( ManageNavPages . LoginCodes ) ;
var code = s . Driver . FindElement ( By . Id ( "logincode" ) ) . GetAttribute ( "value" ) ;
s . Driver . FindElement ( By . Id ( "regeneratecode" ) ) . Click ( ) ;
Assert . NotEqual ( code , s . Driver . FindElement ( By . Id ( "logincode" ) ) . GetAttribute ( "value" ) ) ;
2021-12-31 08:59:02 +01:00
2021-12-24 09:27:00 +01:00
code = s . Driver . FindElement ( By . Id ( "logincode" ) ) . GetAttribute ( "value" ) ;
s . Logout ( ) ;
s . GoToLogin ( ) ;
s . Driver . SetAttribute ( "LoginCode" , "value" , "bad code" ) ;
s . Driver . InvokeJSFunction ( "logincode-form" , "submit" ) ;
2021-12-31 08:59:02 +01:00
2021-12-24 09:27:00 +01:00
s . Driver . SetAttribute ( "LoginCode" , "value" , code ) ;
s . Driver . InvokeJSFunction ( "logincode-form" , "submit" ) ;
2023-07-19 15:21:16 +02:00
s . GoToHome ( ) ;
2021-12-24 09:27:00 +01:00
Assert . Contains ( user , s . Driver . PageSource ) ;
}
2021-12-31 08:59:02 +01:00
2021-11-26 07:07:54 +01:00
// For god know why, selenium have problems clicking on the save button, resulting in ultimate hacks
// to make it works.
private void SudoForceSaveLightningSettingsRightNowAndFast ( SeleniumTester s , string cryptoCode )
{
int maxAttempts = 5 ;
2021-11-27 03:40:06 +01:00
retry :
2021-11-26 07:07:54 +01:00
s . Driver . WaitForAndClick ( By . Id ( "save" ) ) ;
try
{
Assert . Contains ( $"{cryptoCode} Lightning settings successfully updated" , s . FindAlertMessage ( ) . Text ) ;
}
catch ( NoSuchElementException ) when ( maxAttempts > 0 )
{
maxAttempts - - ;
goto retry ;
}
}
2021-11-11 13:03:08 +01:00
[Fact]
[Trait("Selenium", "Selenium")]
public async Task CanUseLNURLAuth ( )
{
using var s = CreateSeleniumTester ( ) ;
await s . StartAsync ( ) ;
var user = s . RegisterNewUser ( true ) ;
2023-07-19 15:21:16 +02:00
s . GoToHome ( ) ;
2021-11-11 13:03:08 +01:00
s . GoToProfile ( ManageNavPages . TwoFactorAuthentication ) ;
s . Driver . FindElement ( By . Name ( "Name" ) ) . SendKeys ( "ln wallet" ) ;
s . Driver . FindElement ( By . Name ( "type" ) )
. FindElement ( By . CssSelector ( $"option[value='{(int)Fido2Credential.CredentialType.LNURLAuth}']" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "btn-add" ) ) . Click ( ) ;
var links = s . Driver . FindElements ( By . CssSelector ( ".tab-content a" ) ) . Select ( element = > element . GetAttribute ( "href" ) ) ;
2023-01-06 14:18:07 +01:00
Assert . Equal ( 2 , links . Count ( ) ) ;
2021-11-11 13:03:08 +01:00
Uri prevEndpoint = null ;
foreach ( string link in links )
{
var endpoint = LNURL . LNURL . Parse ( link , out var tag ) ;
2023-01-06 14:18:07 +01:00
Assert . Equal ( "login" , tag ) ;
if ( endpoint . Scheme ! = "https" )
2021-11-11 13:03:08 +01:00
prevEndpoint = endpoint ;
}
var linkingKey = new Key ( ) ;
var request = Assert . IsType < LNAuthRequest > ( await LNURL . LNURL . FetchInformation ( prevEndpoint , null ) ) ;
_ = await request . SendChallenge ( linkingKey , new HttpClient ( ) ) ;
2023-01-06 14:18:07 +01:00
TestUtils . Eventually ( ( ) = > s . FindAlertMessage ( ) ) ;
2023-05-10 11:18:29 +02:00
s . CreateNewStore ( ) ; // create a store to prevent redirect after login
2021-11-11 13:03:08 +01:00
s . Logout ( ) ;
2022-02-07 13:18:22 +01:00
s . LogIn ( user , "123456" ) ;
2021-11-11 13:03:08 +01:00
var section = s . Driver . FindElement ( By . Id ( "lnurlauth-section" ) ) ;
2023-05-10 11:18:29 +02:00
links = section . FindElements ( By . CssSelector ( ".tab-content a" ) ) . Select ( element = > element . GetAttribute ( "href" ) ) . ToList ( ) ;
2023-01-06 14:18:07 +01:00
Assert . Equal ( 2 , links . Count ( ) ) ;
2021-11-11 13:03:08 +01:00
prevEndpoint = null ;
foreach ( string link in links )
{
var endpoint = LNURL . LNURL . Parse ( link , out var tag ) ;
2023-01-06 14:18:07 +01:00
Assert . Equal ( "login" , tag ) ;
if ( endpoint . Scheme ! = "https" )
2021-11-11 13:03:08 +01:00
prevEndpoint = endpoint ;
}
request = Assert . IsType < LNAuthRequest > ( await LNURL . LNURL . FetchInformation ( prevEndpoint , null ) ) ;
_ = await request . SendChallenge ( linkingKey , new HttpClient ( ) ) ;
TestUtils . Eventually ( ( ) = >
{
2023-05-10 11:18:29 +02:00
Assert . StartsWith ( s . ServerUri . ToString ( ) , s . Driver . Url ) ;
2021-11-11 13:03:08 +01:00
} ) ;
}
2024-03-19 14:58:33 +01:00
2023-05-26 16:49:32 +02:00
[Fact]
[Trait("Selenium", "Selenium")]
public async Task CanUseRoleManager ( )
{
using var s = CreateSeleniumTester ( newDb : true ) ;
await s . StartAsync ( ) ;
2023-07-19 15:21:16 +02:00
s . RegisterNewUser ( true ) ;
s . GoToHome ( ) ;
2023-05-26 16:49:32 +02:00
s . GoToServer ( ServerNavPages . Roles ) ;
var existingServerRoles = s . Driver . FindElement ( By . CssSelector ( "table" ) ) . FindElements ( By . CssSelector ( "tr" ) ) ;
2024-03-19 14:58:33 +01:00
Assert . Equal ( 5 , existingServerRoles . Count ) ;
2023-05-26 16:49:32 +02:00
IWebElement ownerRow = null ;
2024-03-19 14:58:33 +01:00
IWebElement managerRow = null ;
IWebElement employeeRow = null ;
2023-05-26 16:49:32 +02:00
IWebElement guestRow = null ;
foreach ( var roleItem in existingServerRoles )
{
if ( roleItem . Text . Contains ( "owner" , StringComparison . InvariantCultureIgnoreCase ) )
{
ownerRow = roleItem ;
}
2024-03-19 14:58:33 +01:00
else if ( roleItem . Text . Contains ( "manager" , StringComparison . InvariantCultureIgnoreCase ) )
{
managerRow = roleItem ;
}
else if ( roleItem . Text . Contains ( "employee" , StringComparison . InvariantCultureIgnoreCase ) )
{
employeeRow = roleItem ;
}
2023-05-26 16:49:32 +02:00
else if ( roleItem . Text . Contains ( "guest" , StringComparison . InvariantCultureIgnoreCase ) )
{
guestRow = roleItem ;
}
}
Assert . NotNull ( ownerRow ) ;
2024-03-19 14:58:33 +01:00
Assert . NotNull ( managerRow ) ;
Assert . NotNull ( employeeRow ) ;
2023-05-26 16:49:32 +02:00
Assert . NotNull ( guestRow ) ;
var ownerBadges = ownerRow . FindElements ( By . CssSelector ( ".badge" ) ) ;
Assert . Contains ( ownerBadges , element = > element . Text . Equals ( "Default" , StringComparison . InvariantCultureIgnoreCase ) ) ;
Assert . Contains ( ownerBadges , element = > element . Text . Equals ( "Server-wide" , StringComparison . InvariantCultureIgnoreCase ) ) ;
2024-03-19 14:58:33 +01:00
var managerBadges = managerRow . FindElements ( By . CssSelector ( ".badge" ) ) ;
Assert . DoesNotContain ( managerBadges , element = > element . Text . Equals ( "Default" , StringComparison . InvariantCultureIgnoreCase ) ) ;
Assert . Contains ( managerBadges , element = > element . Text . Equals ( "Server-wide" , StringComparison . InvariantCultureIgnoreCase ) ) ;
var employeeBadges = employeeRow . FindElements ( By . CssSelector ( ".badge" ) ) ;
Assert . DoesNotContain ( employeeBadges , element = > element . Text . Equals ( "Default" , StringComparison . InvariantCultureIgnoreCase ) ) ;
Assert . Contains ( employeeBadges , element = > element . Text . Equals ( "Server-wide" , StringComparison . InvariantCultureIgnoreCase ) ) ;
2023-05-26 16:49:32 +02:00
var guestBadges = guestRow . FindElements ( By . CssSelector ( ".badge" ) ) ;
Assert . DoesNotContain ( guestBadges , element = > element . Text . Equals ( "Default" , StringComparison . InvariantCultureIgnoreCase ) ) ;
Assert . Contains ( guestBadges , element = > element . Text . Equals ( "Server-wide" , StringComparison . InvariantCultureIgnoreCase ) ) ;
guestRow . FindElement ( By . Id ( "SetDefault" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
existingServerRoles = s . Driver . FindElement ( By . CssSelector ( "table" ) ) . FindElements ( By . CssSelector ( "tr" ) ) ;
foreach ( var roleItem in existingServerRoles )
{
if ( roleItem . Text . Contains ( "owner" , StringComparison . InvariantCultureIgnoreCase ) )
{
ownerRow = roleItem ;
}
else if ( roleItem . Text . Contains ( "guest" , StringComparison . InvariantCultureIgnoreCase ) )
{
guestRow = roleItem ;
}
}
guestBadges = guestRow . FindElements ( By . CssSelector ( ".badge" ) ) ;
Assert . Contains ( guestBadges , element = > element . Text . Equals ( "Default" , StringComparison . InvariantCultureIgnoreCase ) ) ;
ownerBadges = ownerRow . FindElements ( By . CssSelector ( ".badge" ) ) ;
Assert . DoesNotContain ( ownerBadges , element = > element . Text . Equals ( "Default" , StringComparison . InvariantCultureIgnoreCase ) ) ;
ownerRow . FindElement ( By . Id ( "SetDefault" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
s . CreateNewStore ( ) ;
s . GoToStore ( StoreNavPages . Roles ) ;
var existingStoreRoles = s . Driver . FindElement ( By . CssSelector ( "table" ) ) . FindElements ( By . CssSelector ( "tr" ) ) ;
2024-03-19 14:58:33 +01:00
Assert . Equal ( 5 , existingStoreRoles . Count ) ;
Assert . Equal ( 4 , existingStoreRoles . Count ( element = > element . Text . Contains ( "Server-wide" , StringComparison . InvariantCultureIgnoreCase ) ) ) ;
2023-05-26 16:49:32 +02:00
foreach ( var roleItem in existingStoreRoles )
{
if ( roleItem . Text . Contains ( "owner" , StringComparison . InvariantCultureIgnoreCase ) )
{
ownerRow = roleItem ;
break ;
}
}
ownerRow . FindElement ( By . LinkText ( "Remove" ) ) . Click ( ) ;
Assert . DoesNotContain ( "ConfirmContinue" , s . Driver . PageSource ) ;
s . Driver . Navigate ( ) . Back ( ) ;
existingStoreRoles = s . Driver . FindElement ( By . CssSelector ( "table" ) ) . FindElements ( By . CssSelector ( "tr" ) ) ;
foreach ( var roleItem in existingStoreRoles )
{
if ( roleItem . Text . Contains ( "guest" , StringComparison . InvariantCultureIgnoreCase ) )
{
guestRow = roleItem ;
break ;
}
}
guestRow . FindElement ( By . LinkText ( "Remove" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "ConfirmContinue" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
s . GoToStore ( StoreNavPages . Roles ) ;
s . Driver . FindElement ( By . Id ( "CreateRole" ) ) . Click ( ) ;
Assert . Contains ( "Create role" , s . Driver . PageSource ) ;
s . Driver . FindElement ( By . Id ( "Save" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Role" ) ) . SendKeys ( "store role" ) ;
s . Driver . FindElement ( By . Id ( "Save" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
existingStoreRoles = s . Driver . FindElement ( By . CssSelector ( "table" ) ) . FindElements ( By . CssSelector ( "tr" ) ) ;
foreach ( var roleItem in existingStoreRoles )
{
if ( roleItem . Text . Contains ( "store role" , StringComparison . InvariantCultureIgnoreCase ) )
{
guestRow = roleItem ;
break ;
}
}
guestBadges = guestRow . FindElements ( By . CssSelector ( ".badge" ) ) ;
Assert . DoesNotContain ( guestBadges , element = > element . Text . Equals ( "server-wide" , StringComparison . InvariantCultureIgnoreCase ) ) ;
s . GoToStore ( StoreNavPages . Users ) ;
var options = s . Driver . FindElements ( By . CssSelector ( "#Role option" ) ) ;
2024-03-19 14:58:33 +01:00
Assert . Equal ( 4 , options . Count ) ;
2023-05-26 16:49:32 +02:00
Assert . Contains ( options , element = > element . Text . Equals ( "store role" , StringComparison . InvariantCultureIgnoreCase ) ) ;
s . CreateNewStore ( ) ;
s . GoToStore ( StoreNavPages . Roles ) ;
existingStoreRoles = s . Driver . FindElement ( By . CssSelector ( "table" ) ) . FindElements ( By . CssSelector ( "tr" ) ) ;
2024-03-19 14:58:33 +01:00
Assert . Equal ( 4 , existingStoreRoles . Count ) ;
Assert . Equal ( 3 , existingStoreRoles . Count ( element = > element . Text . Contains ( "Server-wide" , StringComparison . InvariantCultureIgnoreCase ) ) ) ;
2023-05-26 16:49:32 +02:00
Assert . Equal ( 0 , existingStoreRoles . Count ( element = > element . Text . Contains ( "store role" , StringComparison . InvariantCultureIgnoreCase ) ) ) ;
s . GoToStore ( StoreNavPages . Users ) ;
options = s . Driver . FindElements ( By . CssSelector ( "#Role option" ) ) ;
2024-03-19 14:58:33 +01:00
Assert . Equal ( 3 , options . Count ) ;
2023-05-26 16:49:32 +02:00
Assert . DoesNotContain ( options , element = > element . Text . Equals ( "store role" , StringComparison . InvariantCultureIgnoreCase ) ) ;
s . GoToStore ( StoreNavPages . Roles ) ;
s . Driver . FindElement ( By . Id ( "CreateRole" ) ) . Click ( ) ;
s . Driver . FindElement ( By . Id ( "Role" ) ) . SendKeys ( "Malice" ) ;
s . Driver . ExecuteJavaScript ( $"document.getElementById('Policies')['{Policies.CanModifyServerSettings}']=new Option('{Policies.CanModifyServerSettings}', '{Policies.CanModifyServerSettings}', true,true);" ) ;
s . Driver . FindElement ( By . Id ( "Save" ) ) . Click ( ) ;
s . FindAlertMessage ( ) ;
Assert . Contains ( "Malice" , s . Driver . PageSource ) ;
Assert . DoesNotContain ( Policies . CanModifyServerSettings , s . Driver . PageSource ) ;
}
2024-03-14 10:25:40 +01:00
[Fact]
[Trait("Selenium", "Selenium")]
[Trait("Lightning", "Lightning")]
public async Task CanAccessUserStoreAsAdmin ( )
{
using var s = CreateSeleniumTester ( newDb : true ) ;
s . Server . ActivateLightning ( ) ;
await s . StartAsync ( ) ;
await s . Server . EnsureChannelsSetup ( ) ;
var storeSettingsPaths = new [ ] { "settings" , "rates" , "checkout" , "tokens" , "users" , "roles" , "webhooks" , "payout-processors" , "payout-processors/onchain-automated/BTC" , "payout-processors/lightning-automated/BTC" , "emails" , "email-settings" , "forms" } ;
// Setup user, store and wallets
s . RegisterNewUser ( ) ;
( _ , string storeId ) = s . CreateNewStore ( ) ;
s . GoToStore ( ) ;
s . GenerateWallet ( isHotWallet : true ) ;
s . AddLightningNode ( LightningConnectionType . CLightning , false ) ;
// Add apps
( _ , string posId ) = s . CreateApp ( "PointOfSale" ) ;
( _ , string crowdfundId ) = s . CreateApp ( "Crowdfund" ) ;
s . Logout ( ) ;
// Setup admin and check access
s . GoToRegister ( ) ;
s . RegisterNewUser ( true ) ;
string GetStorePath ( string subPath ) = > $"/stores/{storeId}/{subPath}" ;
2024-03-19 14:58:33 +01:00
// Admin access
2024-03-14 10:25:40 +01:00
s . AssertPageAccess ( false , GetStorePath ( "" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "reports" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "invoices" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "invoices/create" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payment-requests" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "payment-requests/edit" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "pull-payments" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payouts" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "onchain/BTC" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "onchain/BTC/settings" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "lightning/BTC" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "lightning/BTC/settings" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "apps/create" ) ) ;
foreach ( var path in storeSettingsPaths )
{ // should have view access to settings, but no submit buttons or create links
2024-03-19 14:58:33 +01:00
TestLogs . LogInformation ( $"Checking access to store page {path} as admin" ) ;
s . AssertPageAccess ( true , $"stores/{storeId}/{path}" ) ;
if ( path ! = "payout-processors" )
{
s . Driver . ElementDoesNotExist ( By . CssSelector ( "#mainContent .btn-primary" ) ) ;
}
}
}
[Fact]
[Trait("Selenium", "Selenium")]
[Trait("Lightning", "Lightning")]
public async Task CanUsePredefinedRoles ( )
{
using var s = CreateSeleniumTester ( newDb : true ) ;
s . Server . ActivateLightning ( ) ;
await s . StartAsync ( ) ;
await s . Server . EnsureChannelsSetup ( ) ;
var storeSettingsPaths = new [ ] { "settings" , "rates" , "checkout" , "tokens" , "users" , "roles" , "webhooks" , "payout-processors" , "payout-processors/onchain-automated/BTC" , "payout-processors/lightning-automated/BTC" , "emails" , "email-settings" , "forms" } ;
// Setup users
var manager = s . RegisterNewUser ( ) ;
s . Logout ( ) ;
s . GoToRegister ( ) ;
var employee = s . RegisterNewUser ( ) ;
s . Logout ( ) ;
s . GoToRegister ( ) ;
var guest = s . RegisterNewUser ( ) ;
s . Logout ( ) ;
s . GoToRegister ( ) ;
// Setup store, wallets and add users
s . RegisterNewUser ( true ) ;
( _ , string storeId ) = s . CreateNewStore ( ) ;
s . GoToStore ( ) ;
s . GenerateWallet ( isHotWallet : true ) ;
s . AddLightningNode ( LightningConnectionType . CLightning , false ) ;
s . AddUserToStore ( storeId , manager , "Manager" ) ;
s . AddUserToStore ( storeId , employee , "Employee" ) ;
s . AddUserToStore ( storeId , guest , "Guest" ) ;
// Add apps
( _ , string posId ) = s . CreateApp ( "PointOfSale" ) ;
( _ , string crowdfundId ) = s . CreateApp ( "Crowdfund" ) ;
string GetStorePath ( string subPath ) = > $"/stores/{storeId}/{subPath}" ;
// Owner access
s . AssertPageAccess ( true , GetStorePath ( "" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "reports" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "invoices" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "invoices/create" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payment-requests" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payment-requests/edit" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "pull-payments" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payouts" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "onchain/BTC" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "onchain/BTC/settings" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "lightning/BTC" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "lightning/BTC/settings" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "apps/create" ) ) ;
s . AssertPageAccess ( true , $"/apps/{posId}/settings/pos" ) ;
s . AssertPageAccess ( true , $"/apps/{crowdfundId}/settings/crowdfund" ) ;
foreach ( var path in storeSettingsPaths )
{ // should have manage access to settings, hence should see submit buttons or create links
TestLogs . LogInformation ( $"Checking access to store page {path} as owner" ) ;
s . AssertPageAccess ( true , $"stores/{storeId}/{path}" ) ;
if ( path ! = "payout-processors" )
{
s . Driver . FindElement ( By . CssSelector ( "#mainContent .btn-primary" ) ) ;
}
}
s . Logout ( ) ;
// Manager access
s . LogIn ( manager ) ;
s . AssertPageAccess ( false , GetStorePath ( "" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "reports" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "invoices" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "invoices/create" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payment-requests" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payment-requests/edit" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "pull-payments" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payouts" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "onchain/BTC" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "onchain/BTC/settings" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "lightning/BTC" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "lightning/BTC/settings" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "apps/create" ) ) ;
s . AssertPageAccess ( true , $"/apps/{posId}/settings/pos" ) ;
s . AssertPageAccess ( true , $"/apps/{crowdfundId}/settings/crowdfund" ) ;
foreach ( var path in storeSettingsPaths )
{ // should have view access to settings, but no submit buttons or create links
TestLogs . LogInformation ( $"Checking access to store page {path} as manager" ) ;
2024-03-14 10:25:40 +01:00
s . AssertPageAccess ( true , $"stores/{storeId}/{path}" ) ;
s . Driver . ElementDoesNotExist ( By . CssSelector ( "#mainContent .btn-primary" ) ) ;
}
2024-03-19 14:58:33 +01:00
s . Logout ( ) ;
// Employee access
s . LogIn ( employee ) ;
s . AssertPageAccess ( false , GetStorePath ( "" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "reports" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "invoices" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "invoices/create" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payment-requests" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payment-requests/edit" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "pull-payments" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payouts" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "onchain/BTC" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "onchain/BTC/settings" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "lightning/BTC" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "lightning/BTC/settings" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "apps/create" ) ) ;
s . AssertPageAccess ( false , $"/apps/{posId}/settings/pos" ) ;
s . AssertPageAccess ( false , $"/apps/{crowdfundId}/settings/crowdfund" ) ;
foreach ( var path in storeSettingsPaths )
{ // should not have access to settings
TestLogs . LogInformation ( $"Checking access to store page {path} as employee" ) ;
s . AssertPageAccess ( false , $"stores/{storeId}/{path}" ) ;
}
s . Logout ( ) ;
// Guest access
s . LogIn ( guest ) ;
s . AssertPageAccess ( false , GetStorePath ( "" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "reports" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "invoices" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "invoices/create" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payment-requests" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "payment-requests/edit" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "pull-payments" ) ) ;
s . AssertPageAccess ( true , GetStorePath ( "payouts" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "onchain/BTC" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "onchain/BTC/settings" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "lightning/BTC" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "lightning/BTC/settings" ) ) ;
s . AssertPageAccess ( false , GetStorePath ( "apps/create" ) ) ;
s . AssertPageAccess ( false , $"/apps/{posId}/settings/pos" ) ;
s . AssertPageAccess ( false , $"/apps/{crowdfundId}/settings/crowdfund" ) ;
foreach ( var path in storeSettingsPaths )
{ // should not have access to settings
TestLogs . LogInformation ( $"Checking access to store page {path} as guest" ) ;
s . AssertPageAccess ( false , $"stores/{storeId}/{path}" ) ;
}
s . Logout ( ) ;
}
[Fact]
[Trait("Selenium", "Selenium")]
public async Task CanChangeUserRoles ( )
{
using var s = CreateSeleniumTester ( newDb : true ) ;
await s . StartAsync ( ) ;
// Setup users and store
var employee = s . RegisterNewUser ( ) ;
s . Logout ( ) ;
s . GoToRegister ( ) ;
var owner = s . RegisterNewUser ( true ) ;
( _ , string storeId ) = s . CreateNewStore ( ) ;
s . GoToStore ( ) ;
s . AddUserToStore ( storeId , employee , "Employee" ) ;
// Should successfully change the role
var userRows = s . Driver . FindElements ( By . CssSelector ( "#StoreUsersList tr" ) ) ;
Assert . Equal ( 2 , userRows . Count ) ;
IWebElement employeeRow = null ;
foreach ( var row in userRows )
{
if ( row . Text . Contains ( employee , StringComparison . InvariantCultureIgnoreCase ) ) employeeRow = row ;
}
Assert . NotNull ( employeeRow ) ;
employeeRow . FindElement ( By . CssSelector ( "a[data-bs-target=\"#EditModal\"]" ) ) . Click ( ) ;
Assert . Equal ( s . Driver . WaitForElement ( By . Id ( "EditUserEmail" ) ) . Text , employee ) ;
new SelectElement ( s . Driver . FindElement ( By . Id ( "EditUserRole" ) ) ) . SelectByValue ( "Manager" ) ;
s . Driver . FindElement ( By . Id ( "EditContinue" ) ) . Click ( ) ;
Assert . Contains ( $"The role of {employee} has been changed to Manager." , s . FindAlertMessage ( ) . Text ) ;
// Should not see a message when not changing role
userRows = s . Driver . FindElements ( By . CssSelector ( "#StoreUsersList tr" ) ) ;
Assert . Equal ( 2 , userRows . Count ) ;
employeeRow = null ;
foreach ( var row in userRows )
{
if ( row . Text . Contains ( employee , StringComparison . InvariantCultureIgnoreCase ) ) employeeRow = row ;
}
Assert . NotNull ( employeeRow ) ;
employeeRow . FindElement ( By . CssSelector ( "a[data-bs-target=\"#EditModal\"]" ) ) . Click ( ) ;
Assert . Equal ( s . Driver . WaitForElement ( By . Id ( "EditUserEmail" ) ) . Text , employee ) ;
// no change, no alert message
s . Driver . FindElement ( By . Id ( "EditContinue" ) ) . Click ( ) ;
s . Driver . ElementDoesNotExist ( By . CssSelector ( "#mainContent .alert" ) ) ;
// Should not change last owner
userRows = s . Driver . FindElements ( By . CssSelector ( "#StoreUsersList tr" ) ) ;
Assert . Equal ( 2 , userRows . Count ) ;
IWebElement ownerRow = null ;
foreach ( var row in userRows )
{
if ( row . Text . Contains ( owner , StringComparison . InvariantCultureIgnoreCase ) ) ownerRow = row ;
}
Assert . NotNull ( ownerRow ) ;
ownerRow . FindElement ( By . CssSelector ( "a[data-bs-target=\"#EditModal\"]" ) ) . Click ( ) ;
Assert . Equal ( s . Driver . WaitForElement ( By . Id ( "EditUserEmail" ) ) . Text , owner ) ;
new SelectElement ( s . Driver . FindElement ( By . Id ( "EditUserRole" ) ) ) . SelectByValue ( "Employee" ) ;
s . Driver . FindElement ( By . Id ( "EditContinue" ) ) . Click ( ) ;
Assert . Contains ( $"User {owner} is the last owner. Their role cannot be changed." , s . FindAlertMessage ( StatusMessageModel . StatusSeverity . Error ) . Text ) ;
2024-03-14 10:25:40 +01:00
}
2023-01-06 14:18:07 +01:00
2021-01-22 17:49:26 +01:00
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 ] ) ;
}
private static void CanSetupEmailCore ( SeleniumTester s )
{
2024-01-26 10:28:50 +01:00
s . Driver . ScrollTo ( By . Id ( "QuickFillDropdownToggle" ) ) ;
2021-05-19 04:39:27 +02:00
s . Driver . FindElement ( By . Id ( "QuickFillDropdownToggle" ) ) . Click ( ) ;
2021-12-11 04:32:23 +01:00
s . Driver . FindElement ( By . CssSelector ( "#quick-fill .dropdown-menu .dropdown-item:first-child" ) ) . Click ( ) ;
2023-12-01 10:50:05 +01:00
s . Driver . FindElement ( By . Id ( "Settings_Login" ) ) . Clear ( ) ;
2021-01-22 17:49:26 +01:00
s . Driver . FindElement ( By . Id ( "Settings_Login" ) ) . SendKeys ( "test@gmail.com" ) ;
2023-12-01 10:50:05 +01:00
s . Driver . FindElement ( By . Id ( "Settings_Password" ) ) . Clear ( ) ;
2021-01-22 17:49:26 +01:00
s . Driver . FindElement ( By . Id ( "Settings_Password" ) ) . SendKeys ( "mypassword" ) ;
2023-12-01 10:50:05 +01:00
s . Driver . FindElement ( By . Id ( "Settings_From" ) ) . Clear ( ) ;
2022-06-23 06:41:52 +02:00
s . Driver . FindElement ( By . Id ( "Settings_From" ) ) . SendKeys ( "Firstname Lastname <email@example.com>" ) ;
2022-01-18 02:19:27 +01:00
s . Driver . FindElement ( By . Id ( "Save" ) ) . SendKeys ( Keys . Enter ) ;
2021-01-22 17:49:26 +01:00
Assert . Contains ( "Configured" , s . Driver . PageSource ) ;
2023-12-01 10:50:05 +01:00
s . Driver . FindElement ( By . Id ( "Settings_Login" ) ) . Clear ( ) ;
2021-01-22 17:49:26 +01:00
s . Driver . FindElement ( By . Id ( "Settings_Login" ) ) . SendKeys ( "test_fix@gmail.com" ) ;
2022-01-18 02:19:27 +01:00
s . Driver . FindElement ( By . Id ( "Save" ) ) . SendKeys ( Keys . Enter ) ;
2021-01-22 17:49:26 +01:00
Assert . Contains ( "Configured" , s . Driver . PageSource ) ;
Assert . Contains ( "test_fix" , s . Driver . PageSource ) ;
2022-01-18 02:19:27 +01:00
s . Driver . FindElement ( By . Id ( "ResetPassword" ) ) . SendKeys ( Keys . Enter ) ;
2021-01-22 17:49:26 +01:00
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 ( ) ;
}
}
2019-05-12 10:13:26 +02:00
}
}