2019-05-21 10:10:07 +02:00
using System ;
2019-09-10 10:03:24 +02:00
using System.Globalization ;
2019-05-13 11:42:20 +02:00
using System.IO ;
2020-06-28 10:55:27 +02:00
using System.Linq ;
using System.Runtime.CompilerServices ;
2019-05-14 12:27:26 +02:00
using System.Threading ;
2019-09-10 10:03:24 +02:00
using System.Threading.Tasks ;
2020-06-28 10:55:27 +02:00
using BTCPayServer ;
2019-09-10 10:03:24 +02:00
using BTCPayServer.Lightning ;
using BTCPayServer.Lightning.CLightning ;
2020-02-13 09:18:43 +01:00
using BTCPayServer.Models ;
2020-04-07 14:18:56 +02:00
using BTCPayServer.Services ;
2020-06-28 10:55:27 +02:00
using BTCPayServer.Tests.Logging ;
2020-02-24 14:36:15 +01:00
using BTCPayServer.Views.Manage ;
2019-09-10 10:03:24 +02:00
using BTCPayServer.Views.Stores ;
2020-04-28 08:06:28 +02:00
using BTCPayServer.Views.Wallets ;
2020-06-28 10:55:27 +02:00
using NBitcoin ;
using OpenQA.Selenium ;
using OpenQA.Selenium.Chrome ;
2019-09-11 07:49:06 +02:00
using OpenQA.Selenium.Interactions ;
2020-06-28 10:55:27 +02:00
using Xunit ;
2019-05-13 11:42:20 +02:00
namespace BTCPayServer.Tests
{
public class SeleniumTester : IDisposable
{
2019-09-11 09:22:41 +02:00
public IWebDriver Driver { get ; set ; }
public ServerTester Server { get ; set ; }
2019-05-13 11:42:20 +02:00
2020-03-19 11:11:15 +01:00
public static SeleniumTester Create ( [ CallerMemberNameAttribute ] string scope = null , bool newDb = false )
2019-05-13 11:42:20 +02:00
{
2020-03-19 11:11:15 +01:00
var server = ServerTester . Create ( scope , newDb ) ;
2019-05-13 11:42:20 +02:00
return new SeleniumTester ( )
{
2019-09-11 09:22:41 +02:00
Server = server
2019-05-13 11:42:20 +02:00
} ;
}
2020-01-12 05:54:06 +01:00
2019-10-07 09:04:25 +02:00
public async Task StartAsync ( )
2019-05-13 11:42:20 +02:00
{
2019-10-07 09:04:25 +02:00
await Server . StartAsync ( ) ;
2019-05-13 11:42:20 +02:00
ChromeOptions options = new ChromeOptions ( ) ;
2019-09-10 10:03:24 +02:00
var isDebug = ! Server . PayTester . InContainer ;
2020-01-12 05:54:06 +01:00
2019-09-10 10:03:24 +02:00
if ( ! isDebug )
{
options . AddArguments ( "headless" ) ; // Comment to view browser
options . AddArguments ( "window-size=1200x1000" ) ; // Comment to view browser
2020-01-12 05:54:06 +01:00
}
2019-05-14 15:29:05 +02:00
options . AddArgument ( "shm-size=2g" ) ;
if ( Server . PayTester . InContainer )
{
options . AddArgument ( "no-sandbox" ) ;
}
2019-09-11 09:22:41 +02:00
Driver = new ChromeDriver ( Server . PayTester . InContainer ? "/usr/bin" : Directory . GetCurrentDirectory ( ) , options ) ;
2019-09-10 10:03:24 +02:00
if ( isDebug )
{
//when running locally, depending on your resolution, the website may go into mobile responsive mode and screw with navigation of tests
Driver . Manage ( ) . Window . Maximize ( ) ;
}
2019-05-14 12:58:28 +02:00
Logs . Tester . LogInformation ( "Selenium: Using chrome driver" ) ;
2019-05-14 10:34:19 +02:00
Logs . Tester . LogInformation ( "Selenium: Browsing to " + Server . PayTester . ServerUri ) ;
2019-05-14 15:29:05 +02:00
Logs . Tester . LogInformation ( $"Selenium: Resolution {Driver.Manage().Window.Size}" ) ;
2019-10-12 13:35:30 +02:00
Driver . Manage ( ) . Timeouts ( ) . ImplicitWait = ImplicitWait ;
2019-11-06 06:31:45 +01:00
GoToRegister ( ) ;
2019-05-14 11:35:22 +02:00
Driver . AssertNoError ( ) ;
2019-05-13 11:42:20 +02:00
}
2020-02-24 14:36:15 +01:00
internal IWebElement AssertHappyMessage ( StatusMessageModel . StatusSeverity severity = StatusMessageModel . StatusSeverity . Success )
2019-11-16 07:20:54 +01:00
{
2020-01-18 07:36:20 +01:00
using var cts = new CancellationTokenSource ( 20_000 ) ;
2020-01-12 07:50:23 +01:00
while ( ! cts . IsCancellationRequested )
2020-01-12 05:54:06 +01:00
{
2020-02-24 14:36:15 +01:00
var result = Driver . FindElements ( By . ClassName ( $"alert-{StatusMessageModel.ToString(severity)}" ) ) . Where ( el = > el . Displayed ) ;
if ( result . Any ( ) )
return result . First ( ) ;
2020-01-12 07:50:23 +01:00
Thread . Sleep ( 100 ) ;
2020-01-12 05:54:06 +01:00
}
2020-01-12 07:50:23 +01:00
Logs . Tester . LogInformation ( this . Driver . PageSource ) ;
2020-02-13 09:18:43 +01:00
Assert . True ( false , $"Should have shown {severity} message" ) ;
2020-02-24 14:36:15 +01:00
return null ;
2020-06-28 10:55:27 +02:00
}
2019-11-16 07:20:54 +01:00
2019-10-12 13:35:30 +02:00
public static readonly TimeSpan ImplicitWait = TimeSpan . FromSeconds ( 10 ) ;
2019-05-14 16:33:46 +02:00
public string Link ( string relativeLink )
{
return Server . PayTester . ServerUri . AbsoluteUri . WithoutEndingSlash ( ) + relativeLink . WithStartingSlash ( ) ;
}
2019-05-13 11:42:20 +02:00
2019-11-06 06:31:45 +01:00
public void GoToRegister ( )
{
Driver . Navigate ( ) . GoToUrl ( this . Link ( "/Account/Register" ) ) ;
}
2019-05-13 11:42:20 +02:00
public string RegisterNewUser ( bool isAdmin = false )
{
2019-09-19 12:17:20 +02:00
var usr = RandomUtils . GetUInt256 ( ) . ToString ( ) . Substring ( 64 - 20 ) + "@a.com" ;
2020-06-24 03:34:09 +02:00
Logs . Tester . LogInformation ( $"User: {usr} with password 123456" ) ;
2019-05-13 11:42:20 +02:00
Driver . FindElement ( By . Id ( "Email" ) ) . SendKeys ( usr ) ;
Driver . FindElement ( By . Id ( "Password" ) ) . SendKeys ( "123456" ) ;
Driver . FindElement ( By . Id ( "ConfirmPassword" ) ) . SendKeys ( "123456" ) ;
2019-05-14 16:33:46 +02:00
if ( isAdmin )
Driver . FindElement ( By . Id ( "IsAdmin" ) ) . Click ( ) ;
2019-05-13 11:42:20 +02:00
Driver . FindElement ( By . Id ( "RegisterButton" ) ) . Click ( ) ;
Driver . AssertNoError ( ) ;
return usr ;
}
2019-09-03 13:11:36 +02:00
public ( string storeName , string storeId ) CreateNewStore ( )
2019-05-13 11:42:20 +02:00
{
var usr = "Store" + RandomUtils . GetUInt64 ( ) . ToString ( ) ;
Driver . FindElement ( By . Id ( "Stores" ) ) . Click ( ) ;
Driver . FindElement ( By . Id ( "CreateStore" ) ) . Click ( ) ;
Driver . FindElement ( By . Id ( "Name" ) ) . SendKeys ( usr ) ;
Driver . FindElement ( By . Id ( "Create" ) ) . Click ( ) ;
2020-06-24 03:34:09 +02:00
StoreId = Driver . FindElement ( By . Id ( "Id" ) ) . GetAttribute ( "value" ) ;
return ( usr , StoreId ) ;
2019-05-13 11:42:20 +02:00
}
2020-06-24 03:34:09 +02:00
public string StoreId { get ; set ; }
2019-05-13 11:42:20 +02:00
2020-04-25 16:11:00 +02:00
public Mnemonic GenerateWallet ( string cryptoCode = "BTC" , string seed = "" , bool importkeys = false , bool privkeys = false , ScriptPubKeyType format = ScriptPubKeyType . Segwit )
2019-12-05 18:56:40 +01:00
{
Driver . FindElement ( By . Id ( $"Modify{cryptoCode}" ) ) . ForceClick ( ) ;
Driver . FindElement ( By . Id ( "import-from-btn" ) ) . ForceClick ( ) ;
Driver . FindElement ( By . Id ( "nbxplorergeneratewalletbtn" ) ) . ForceClick ( ) ;
2020-03-06 09:06:32 +01:00
Driver . WaitForElement ( By . Id ( "ExistingMnemonic" ) ) . SendKeys ( seed ) ;
2020-03-21 18:23:02 +01:00
SetCheckbox ( Driver . WaitForElement ( By . Id ( "SavePrivateKeys" ) ) , privkeys ) ;
SetCheckbox ( Driver . WaitForElement ( By . Id ( "ImportKeysToRPC" ) ) , importkeys ) ;
2020-04-25 16:11:00 +02:00
Driver . WaitForElement ( By . Id ( "ScriptPubKeyType" ) ) . Click ( ) ;
Driver . WaitForElement ( By . CssSelector ( $"#ScriptPubKeyType option[value={format}]" ) ) . Click ( ) ;
2020-03-21 18:23:02 +01:00
Logs . Tester . LogInformation ( "Trying to click btn-generate" ) ;
Driver . WaitForElement ( By . Id ( "btn-generate" ) ) . ForceClick ( ) ;
2020-07-15 23:58:18 +02:00
// Seed backup page
2019-12-05 18:56:40 +01:00
AssertHappyMessage ( ) ;
if ( string . IsNullOrEmpty ( seed ) )
{
2020-07-15 23:58:18 +02:00
seed = Driver . FindElements ( By . Id ( "recovery-phrase" ) ) . First ( ) . GetAttribute ( "data-mnemonic" ) ;
2019-12-05 18:56:40 +01:00
}
2020-07-15 23:58:18 +02:00
// Confirm seed backup
Driver . FindElement ( By . Id ( "confirm" ) ) . Click ( ) ;
Driver . FindElement ( By . Id ( "submit" ) ) . Click ( ) ;
2020-06-24 03:34:09 +02:00
WalletId = new WalletId ( StoreId , cryptoCode ) ;
2020-03-23 07:46:54 +01:00
return new Mnemonic ( seed ) ;
2019-12-05 18:56:40 +01:00
}
2020-06-24 03:34:09 +02:00
public WalletId WalletId { get ; set ; }
2019-09-10 10:03:24 +02:00
public void AddDerivationScheme ( string cryptoCode = "BTC" , string derivationScheme = "xpub661MyMwAqRbcGABgHMUXDzPzH1tU7eZaAaJQXhDXsSxsqyQzQeU6kznNfSuAyqAK9UaWSaZaMFdNiY5BCF4zBPAzSnwfUAwUhwttuAKwfRX-[legacy]" )
2019-05-13 11:42:20 +02:00
{
2019-09-10 10:03:24 +02:00
Driver . FindElement ( By . Id ( $"Modify{cryptoCode}" ) ) . ForceClick ( ) ;
2019-05-25 04:45:36 +02:00
Driver . FindElement ( By . ClassName ( "store-derivation-scheme" ) ) . SendKeys ( derivationScheme ) ;
2019-05-14 15:29:05 +02:00
Driver . FindElement ( By . Id ( "Continue" ) ) . ForceClick ( ) ;
Driver . FindElement ( By . Id ( "Confirm" ) ) . ForceClick ( ) ;
2019-11-16 07:20:54 +01:00
AssertHappyMessage ( ) ;
2019-05-13 11:42:20 +02:00
}
2020-01-12 05:54:06 +01:00
2019-09-10 10:03:24 +02:00
public void AddLightningNode ( string cryptoCode , LightningConnectionType connectionType )
{
string connectionString = null ;
if ( connectionType = = LightningConnectionType . Charge )
connectionString = "type=charge;server=" + Server . MerchantCharge . Client . Uri . AbsoluteUri ;
else if ( connectionType = = LightningConnectionType . CLightning )
connectionString = "type=clightning;server=" + ( ( CLightningClient ) Server . MerchantLightningD ) . Address . AbsoluteUri ;
else if ( connectionType = = LightningConnectionType . LndREST )
connectionString = $"type=lnd-rest;server={Server.MerchantLnd.Swagger.BaseUrl};allowinsecure=true" ;
else
throw new NotSupportedException ( connectionType . ToString ( ) ) ;
2020-01-12 05:54:06 +01:00
2019-09-10 10:03:24 +02:00
Driver . FindElement ( By . Id ( $"Modify-Lightning{cryptoCode}" ) ) . ForceClick ( ) ;
Driver . FindElement ( By . Name ( $"ConnectionString" ) ) . SendKeys ( connectionString ) ;
Driver . FindElement ( By . Id ( $"save" ) ) . ForceClick ( ) ;
}
2020-01-12 05:54:06 +01:00
2019-09-10 10:03:24 +02:00
public void AddInternalLightningNode ( string cryptoCode )
{
Driver . FindElement ( By . Id ( $"Modify-Lightning{cryptoCode}" ) ) . ForceClick ( ) ;
Driver . FindElement ( By . Id ( $"internal-ln-node-setter" ) ) . ForceClick ( ) ;
Driver . FindElement ( By . Id ( $"save" ) ) . ForceClick ( ) ;
}
2019-05-13 11:42:20 +02:00
public void ClickOnAllSideMenus ( )
{
var links = Driver . FindElements ( By . CssSelector ( ".nav-pills .nav-link" ) ) . Select ( c = > c . GetAttribute ( "href" ) ) . ToList ( ) ;
2019-05-15 09:00:03 +02:00
Driver . AssertNoError ( ) ;
2019-05-13 11:42:20 +02:00
Assert . NotEmpty ( links ) ;
foreach ( var l in links )
{
2019-10-31 04:29:59 +01:00
Logs . Tester . LogInformation ( $"Checking no error on {l}" ) ;
2019-05-13 11:42:20 +02:00
Driver . Navigate ( ) . GoToUrl ( l ) ;
Driver . AssertNoError ( ) ;
}
}
2020-01-12 05:54:06 +01:00
2019-05-13 11:42:20 +02:00
public void Dispose ( )
{
if ( Driver ! = null )
{
try
{
Driver . Close ( ) ;
}
catch { }
Driver . Dispose ( ) ;
}
if ( Server ! = null )
Server . Dispose ( ) ;
}
2019-05-14 16:33:46 +02:00
internal void AssertNotFound ( )
{
2020-02-01 09:29:08 +01:00
Assert . Contains ( "404 - Page not found</h1>" , Driver . PageSource ) ;
2019-05-14 16:33:46 +02:00
}
2019-07-01 05:39:25 +02:00
public void GoToHome ( )
2019-05-14 16:33:46 +02:00
{
Driver . Navigate ( ) . GoToUrl ( Server . PayTester . ServerUri ) ;
}
2019-07-01 05:39:25 +02:00
public void Logout ( )
2019-05-14 16:33:46 +02:00
{
Driver . FindElement ( By . Id ( "Logout" ) ) . Click ( ) ;
}
2019-07-01 05:39:25 +02:00
public void Login ( string user , string password )
{
Driver . FindElement ( By . Id ( "Email" ) ) . SendKeys ( user ) ;
Driver . FindElement ( By . Id ( "Password" ) ) . SendKeys ( password ) ;
Driver . FindElement ( By . Id ( "LoginButton" ) ) . Click ( ) ;
2019-09-11 09:22:27 +02:00
2019-07-01 05:39:25 +02:00
}
2019-09-03 13:11:36 +02:00
2019-09-10 10:03:24 +02:00
public void GoToStore ( string storeId , StoreNavPages storeNavPage = StoreNavPages . Index )
2019-09-03 13:11:36 +02:00
{
Driver . FindElement ( By . Id ( "Stores" ) ) . Click ( ) ;
Driver . FindElement ( By . Id ( $"update-store-{storeId}" ) ) . Click ( ) ;
2019-09-10 10:03:24 +02:00
if ( storeNavPage ! = StoreNavPages . Index )
{
Driver . FindElement ( By . Id ( storeNavPage . ToString ( ) ) ) . Click ( ) ;
}
2019-09-03 13:11:36 +02:00
}
2020-01-12 05:54:06 +01:00
2019-09-03 13:11:36 +02:00
public void GoToInvoiceCheckout ( string invoiceId )
{
Driver . FindElement ( By . Id ( "Invoices" ) ) . Click ( ) ;
Driver . FindElement ( By . Id ( $"invoice-checkout-{invoiceId}" ) ) . Click ( ) ;
2019-09-10 10:03:24 +02:00
CheckForJSErrors ( ) ;
2019-09-03 13:11:36 +02:00
}
2020-01-12 05:54:06 +01:00
2019-09-03 13:11:36 +02:00
public void SetCheckbox ( IWebElement element , bool value )
{
if ( ( value & & ! element . Selected ) | | ( ! value & & element . Selected ) )
{
element . Click ( ) ;
}
2019-12-16 12:10:03 +01:00
if ( value ! = element . Selected )
{
2020-03-21 18:23:02 +01:00
Logs . Tester . LogInformation ( "SetCheckbox recursion, trying to click again" ) ;
2019-12-16 12:10:03 +01:00
SetCheckbox ( element , value ) ;
}
2019-09-03 13:11:36 +02:00
}
2020-03-21 18:23:02 +01:00
public void SetCheckbox ( SeleniumTester s , string checkboxId , bool value )
2019-09-03 13:11:36 +02:00
{
2020-03-21 18:23:02 +01:00
SetCheckbox ( s . Driver . WaitForElement ( By . Id ( checkboxId ) ) , value ) ;
2019-09-03 13:11:36 +02:00
}
2019-09-10 10:03:24 +02:00
2019-09-11 07:49:06 +02:00
public void ScrollToElement ( IWebElement element )
{
Actions actions = new Actions ( Driver ) ;
actions . MoveToElement ( element ) ;
actions . Perform ( ) ;
}
2019-09-10 10:03:24 +02:00
public void GoToInvoices ( )
{
Driver . FindElement ( By . Id ( "Invoices" ) ) . Click ( ) ;
}
2020-06-28 10:55:27 +02:00
public void GoToProfile ( ManageNavPages navPages = ManageNavPages . Index )
2020-02-24 14:36:15 +01:00
{
Driver . FindElement ( By . Id ( "MySettings" ) ) . Click ( ) ;
if ( navPages ! = ManageNavPages . Index )
{
Driver . FindElement ( By . Id ( navPages . ToString ( ) ) ) . Click ( ) ;
}
}
public void GoToLogin ( )
{
Driver . Navigate ( ) . GoToUrl ( new Uri ( Server . PayTester . ServerUri , "Account/Login" ) ) ;
}
2019-09-10 10:03:24 +02:00
public void GoToCreateInvoicePage ( )
{
GoToInvoices ( ) ;
Driver . FindElement ( By . Id ( "CreateNewInvoice" ) ) . Click ( ) ;
}
2020-04-25 18:47:47 +02:00
public string CreateInvoice ( string storeName , decimal amount = 100 , string currency = "USD" , string refundEmail = "" )
2019-09-10 10:03:24 +02:00
{
GoToInvoices ( ) ;
Driver . FindElement ( By . Id ( "CreateNewInvoice" ) ) . Click ( ) ;
Driver . FindElement ( By . Id ( "Amount" ) ) . SendKeys ( amount . ToString ( CultureInfo . InvariantCulture ) ) ;
var currencyEl = Driver . FindElement ( By . Id ( "Currency" ) ) ;
currencyEl . Clear ( ) ;
currencyEl . SendKeys ( currency ) ;
Driver . FindElement ( By . Id ( "BuyerEmail" ) ) . SendKeys ( refundEmail ) ;
2020-04-25 18:47:47 +02:00
Driver . FindElement ( By . Name ( "StoreId" ) ) . SendKeys ( storeName + Keys . Enter ) ;
2019-09-10 10:03:24 +02:00
Driver . FindElement ( By . Id ( "Create" ) ) . ForceClick ( ) ;
Assert . True ( Driver . PageSource . Contains ( "just created!" ) , "Unable to create Invoice" ) ;
var statusElement = Driver . FindElement ( By . ClassName ( "alert-success" ) ) ;
var id = statusElement . Text . Split ( " " ) [ 1 ] ;
return id ;
}
2020-06-24 03:34:09 +02:00
public async Task FundStoreWallet ( WalletId walletId = null , int coins = 1 , decimal denomination = 1 m )
2020-03-30 08:31:30 +02:00
{
2020-06-24 03:34:09 +02:00
walletId ? ? = WalletId ;
2020-04-28 08:06:28 +02:00
GoToWallet ( walletId , WalletsNavPages . Receive ) ;
2020-03-30 08:31:30 +02:00
Driver . FindElement ( By . Id ( "generateButton" ) ) . Click ( ) ;
var addressStr = Driver . FindElement ( By . Id ( "vue-address" ) ) . GetProperty ( "value" ) ;
var address = BitcoinAddress . Create ( addressStr , ( ( BTCPayNetwork ) Server . NetworkProvider . GetNetwork ( walletId . CryptoCode ) ) . NBitcoinNetwork ) ;
for ( int i = 0 ; i < coins ; i + + )
{
await Server . ExplorerNode . SendToAddressAsync ( address , Money . Coins ( denomination ) ) ;
}
}
2020-06-28 10:55:27 +02:00
2020-03-30 08:31:30 +02:00
public void PayInvoice ( WalletId walletId , string invoiceId )
{
GoToInvoiceCheckout ( invoiceId ) ;
var bip21 = Driver . FindElement ( By . ClassName ( "payment__details__instruction__open-wallet__btn" ) )
. GetAttribute ( "href" ) ;
2020-04-07 14:18:56 +02:00
Assert . Contains ( $"{PayjoinClient.BIP21EndpointKey}" , bip21 ) ;
2020-06-28 10:55:27 +02:00
2020-04-28 08:06:28 +02:00
GoToWallet ( walletId , WalletsNavPages . Send ) ;
2020-03-30 08:31:30 +02:00
Driver . FindElement ( By . Id ( "bip21parse" ) ) . Click ( ) ;
Driver . SwitchTo ( ) . Alert ( ) . SendKeys ( bip21 ) ;
Driver . SwitchTo ( ) . Alert ( ) . Accept ( ) ;
Driver . ScrollTo ( By . Id ( "SendMenu" ) ) ;
Driver . FindElement ( By . Id ( "SendMenu" ) ) . ForceClick ( ) ;
Driver . FindElement ( By . CssSelector ( "button[value=nbx-seed]" ) ) . Click ( ) ;
Driver . FindElement ( By . CssSelector ( "button[value=broadcast]" ) ) . ForceClick ( ) ;
}
2019-09-10 10:03:24 +02:00
2020-06-28 10:55:27 +02:00
2020-01-12 05:54:06 +01:00
2019-09-10 10:03:24 +02:00
private void CheckForJSErrors ( )
{
//wait for seleniun update: https://stackoverflow.com/questions/57520296/selenium-webdriver-3-141-0-driver-manage-logs-availablelogtypes-throwing-syste
2020-01-12 05:54:06 +01:00
// var errorStrings = new List<string>
// {
// "SyntaxError",
// "EvalError",
// "ReferenceError",
// "RangeError",
// "TypeError",
// "URIError"
// };
//
// var jsErrors = Driver.Manage().Logs.GetLog(LogType.Browser).Where(x => errorStrings.Any(e => x.Message.Contains(e)));
//
// if (jsErrors.Any())
// {
// Logs.Tester.LogInformation("JavaScript error(s):" + Environment.NewLine + jsErrors.Aggregate("", (s, entry) => s + entry.Message + Environment.NewLine));
// }
// Assert.Empty(jsErrors);
2019-09-10 10:03:24 +02:00
}
2020-06-24 03:34:09 +02:00
public void GoToWallet ( WalletId walletId = null , WalletsNavPages navPages = WalletsNavPages . Send )
2020-03-23 07:46:54 +01:00
{
2020-06-24 03:34:09 +02:00
walletId ? ? = WalletId ;
2020-04-28 08:06:28 +02:00
Driver . Navigate ( ) . GoToUrl ( new Uri ( Server . PayTester . ServerUri , $"wallets/{walletId}" ) ) ;
if ( navPages ! = WalletsNavPages . Transactions )
{
Driver . FindElement ( By . Id ( $"Wallet{navPages}" ) ) . Click ( ) ;
}
2020-03-23 07:46:54 +01:00
}
2020-06-24 10:51:00 +02:00
public void GoToInvoice ( string id )
{
GoToInvoices ( ) ;
2020-06-28 10:55:27 +02:00
foreach ( var el in Driver . FindElements ( By . ClassName ( "invoice-details-link" ) ) )
2020-06-24 10:51:00 +02:00
{
if ( el . GetAttribute ( "href" ) . Contains ( id , StringComparison . OrdinalIgnoreCase ) )
{
el . Click ( ) ;
break ;
}
}
}
2019-05-13 11:42:20 +02:00
}
}