Below is the code I was using to at least select an element from the object. The image attached shows what the object look like on the web page. There is no unique name for me to use in selecting the object. With this code, I was able to see the seat count, but applying the click action on any of the seats did not work. Any idea in the right direction would be appreciated.
public static void SeatSelection()
{
IList<IWebElement> seats = Driver.FindElements(By.TagName("circle"));
foreach (IWebElement seat in seats)
{
if (seat.Displayed && seat.Enabled == true)
{
seat.Click();
}
else
{
Console.WriteLine("Tickets Sold Out");
}
}
}
Related
I have an object which is defined as Pane in FlaUInspect with a number of Checkboxes loaded dynamically when the program starts. For a Unit Test I need to loop through all the checkBoxes and find one particular item based upon a string. The code below was first attempt to load the item which does load but it doesn't list all the checkboxes in the Pane.
ListBox seqPanelItems = databaseWindow.FindFirstDescendant(cf => cf.ByAutomationId("sequenceScrollViewer")).AsListBox();
var rdctSeqCheckBox = GetSeqPanelCheckbox(seqPanelItems, "RDCT");
The following code is what loops through the items.
private CheckBox GetSeqPanelCheckbox(ListBox items, string name)
{
for (int i = 0; i < items.Items.Length; i += 1)
{
//if (items[i] is not Label)
//{
// continue;
//}
if (items.Items[i].Name == name)
{
return items.Items[i - 1].AsCheckBox();
}
}
return null;
}
As the code indicates, it needs to find the particular item and return it as a checkbox item but items.Items.Length returns 0.
Below is what FlaUInspect shows.
I am considering this problem closed. I moved the CheckBox and Text to a list box.
My app is in VS2022 C# Blazor WASM-hosted with .Net 6. I have a blazor page with a sub-Component that has functionality to allow the PAGE to see a select-box for Active/Inactive (two options) and a select-box for a list of Customer names (abbreviated).
The PAGE looks like this:
The TOP rectangle is the sub-component that "acts" like a "function" that returns (via CALLBACK) the selected "CustomerID" (shown is 'ARCTB'). The sub-component reacts to the "select-choice" (#onchange=) by CALLBACK-parameter to the parent form with the 'selected' CustomerID so the parent-page can fetch the vehicles for that selected customer.
On the initial sub-component OnParametersSetAsync(), where the list of customers and first CustomerID are determined, the CALLBACK to the PAGE is made using the List[0] element for CustomerID. At this point at the PAGE, a re-render takes place for both the PAGE and the sub-component. That causes the sub-component's OnParametersSetAsync() to fire again and the CALLBACK, again for the infinite loop.
The SECOND rectangle is part of the PAGE-html and allows the user to select active or inactive vehicles, that filters the list of customer vehicles to that of _listOfAllVehicles( v => v.IS_ACTIVE == true-or-false).
The problem of infinite-loop occurs because the CALLBACK to the PAGE forces re-renders and then the sub-component performs another CALLBACK from the OnParametersSetAsync() method. This is the infinite-loop problem I seek an answer.
Being new to the latest Blazor, I am not sure if ShouldRender() could be used on the PAGE or sub-component or both? Maybe using a sub-component "flag" to denote "OkToCallback" to limit repeated CALLBACK calls.
Your comments, questions and answers are welcome.
As requested by #Marius, the sub-component code is shown below. I have included a TEST for '_IsLoading' that STOPPED the infinite-loop.
Thank you #Marius for the ANSWER. :-)
protected override async Task OnParametersSetAsync() {
// Retain the Parameters.
_LoggedInCustomerName = pLoggedInCustomerName;
_LoggedInUserRoleName = pLoggedInRole;
_IsUserNotFCI = (_LoggedInUserRoleName != "FCI");
_IsActiveCustomers = (_ddlActive_Value == 1);
await Task.Delay(10);
_ddlCustomer_Value = RefreshCustomerList(_ddlActive_Value);
if (_ddlCustomer_Value > 0) {
// Set to first-option (index) (refer to wwwroot\index.html -- see OnAfterRender()
if (_IsLoading) {
// Update the selected customer.
ChangeEventArgs evntArgs = new ChangeEventArgs();
evntArgs.Value = _ddlCustomer_Value.ToString();
await SelectedCustomerChanged(evntArgs);
_IsLoading = false;
}
}
base.OnParametersSet();
}
private int RefreshCustomerList(int pCustomersActiveFlag) {
// Refresh the customer-list based on the 'ddlActiveFilter' ACTIVE-STATUS and USER-ROLE.
// Return the zero-th element's UID_CUSTOMER of the list of customers.
bool isActiveFlag = (pCustomersActiveFlag==1);
int retValue = 0;
if (_ListAllCustomers != null) {
// When LOGGEDIN-USER is NOT FCI, reduce the list to ONLY the loggedin-user customer.
if (_IsUserNotFCI) {
// This returns a list of a single <Customer> record.
_ListFilteredCustomers = _ListAllCustomers.Where(c => (c.TXT_CUSTOMER_NAME == _LoggedInCustomerName)).ToList();
} else {
// This returns a list of all ACTIVE or INACTIVE customers based on 'pCustomersActiveFlag' => 'isActiveFlag'.
_ListFilteredCustomers = _ListAllCustomers.Where(c => (c.BOOL_IS_CUST_ACTIVE == isActiveFlag)).OrderBy(r => r.TXT_CUSTOMER_ABBREV).ToList();
}
// Set the 'ddlCustomer_Value' to the UID of the first-customer in the list.
retValue = _ListFilteredCustomers[0].UID_CUSTOMER;
}
return retValue;
}
private async Task SelectedCustomerChanged(ChangeEventArgs e) { // This is the event after selection is made.
int iArgValue = Convert.ToInt32(e.Value);
_ddlCustomer_Value = Convert.ToInt32(e.Value);
Customer customer = _ListFilteredCustomers!.Where(rec => rec.UID_CUSTOMER == _ddlCustomer_Value).FirstOrDefault();
if (customer != null) {
_SelectedCustUID = customer.UID_CUSTOMER;
_SelectedCustName = customer.TXT_CUSTOMER_NAME;
}
// Callback to Parent.
await OnCustomerSelection.InvokeAsync(_ddlCustomer_Value);
}
OnParametersSetAsync and OnParametersSet will fire whenever a parameter changes, even if the value is the same as before. You can write some code to avoid this.
Example: Is previous parameter value the same as current ? skip load : load
private string _cachedValue;
[Parameter]
public string parameterValue { get; set; }
protected override async Task OnParametersSetAsync()
{
if (parameterValue != _cachedValue)
{
_cachedValue = parameterValue;
LoadComponent();
}
}
private void LoadComponent() {
//...
}
The above is just an example of one method of doing this. Use logic that suits your code.
So,
In my WPF application, I want my users to be able to open previews of invoices, so that they may either verify or discard them. I am letting them check rows (each row representing a invoice) in a DataGridCheckBoxColumn in my DataGrid, then clicking a button (which runs my CreateInvoicePreview() method, see bottom of post), having all of the invoice previews be opened in new windows (one window for each invoice).
Well.. What happens now, is: User checks InvoiceA and InvoiceB. Two invoices are opened, but they are the same: InvoiceC. The correct amount of invoices are always opened, but not the correct instance. If I open the temp folder specified in my file path, I see that all invoices in the datagrid has been saved: InvoiceA through InvoiceJ.
Let me take you through the code.
This is the method that creates that builds and saves the actual PDF's, which the WebView2 control uses as source, so that it can display them in-app. It is heavily abbreviated.
I have kept the structure with the nested foreach loops in case that is relevant.
public void CreatePreviewInvoice() {
/* SQL SERVER CODE
* SQL SERVER CODE
* SQL SERVER CODE */
List<PaidTrip> paidTrips = PaidTrips.ToList();
tripsGroupedByCompany = paidTrips.GroupBy(pt => pt.LicenseHolderID);
foreach (IGrouping<string, PaidTrip> companyGroup in tripsGroupedByCompany) {
/* SQL SERVER CODE
* SQL SERVER CODE
* SQL SERVER CODE */
List<LicenseHolder> licenseHolders = LicenseHolders.ToList();
IEnumerable<IGrouping<string, PaidTrip>> groupedByVehicle = companyGroup.GroupBy(n => n.VehicleID);
foreach (IGrouping<string, PaidTrip> vehicleGroup in groupedByVehicle) {
// Iterating over all data pertaining to each vehicle
foreach (PaidTrip trip in vehicleGroup) {
}
try {
string userName = System.Security.Principal.WindowsIdentity.GetCurrent().Name.Split('\\')[1];
string fileName = $"FORHÅNDSVISNING - MÅ IKKE SENDES! {LicenseHolderID + "_" + "Faktura_" + InvoiceID}.pdf";
string filePath = $#"C:\Users\{userName}\AppData\Local\Temp\";
PdfFilePath = $"{filePath}{fileName}";
//if (LicenseHolderID == PreviewInvoiceViewModel.SelectedRow.LicenseHolderID) {
document.Save($"{PdfFilePath}");
//} else {
// return;
//}
} catch (Exception ex) {
MessageBox.Show(ex.Message);
}
}
}
As you see, towards the end of the method I have commented out a bit of code, which was me trying to implement a way to filter based on the checked rows only. It did not work.
This is the XAML for the WebView2:
<Wpf:WebView2
x:Name="wv_preview_invoice" Loaded="{s:Action CreatePreviewInvoice}"
Height="997" Width="702" Canvas.Left="20" Canvas.Top="71"
Source="{Binding PdfFilePath}"></Wpf:WebView2>
PdfFilePath is a property, which is referenced within the method above.
It's given a value within the method, and when Source (for the WebView2) is called, it is able to get the value from PdfFilePath, and thus display the PDF.
But as I said initially, it just creates X amount of instances/windows of the same invoice. Always the same one, because of in what order they are queried from the database.
And finally, here is the method that run when they click whichever invoices they want to preview, it's to open the new window with the WebView2 control:
public void PreviewInvoices() {
bool success = false;
foreach (PaidTrip item in PaidTrips) {
if (item.IsChecked == true) {
ShowPreviewInvoiceDetailed(item);
success = true;
}
}
if (!success) {
MessageBox.Show("You must chose an invoice to preview first.");
}
}
The method that opens the next window where the WebView2 is, looks like this:
public void ShowPreviewInvoiceDetailed() {
PreviewInvoiceDetailedViewModel viewModel = new(windowManager);
windowManager.ShowWindow(viewModel);
}
What part (or several parts) of the picture am I missing?
I managed to solve this by doing the following:
I made a property; public static string PreviewedInvoice { get; set; } in the ViewModel of the parent window. In my method that opens the child window (where the preview invoices are to be displayed) I bind it to LicenseHolderID of the rows that have a checked CheckBox, via foreach loop, like such:
public void PreviewInvoices() {
bool success = false;
foreach (PaidTrip item in PaidTrips) {
if (item.IsChecked == true) {
PreviewedInvoice = item.LicenseHolderID;
ShowPreviewInvoiceDetailed();
success = true;
}
}
if (!success) {
MessageBox.Show("You must chose an invoice to preview first.");
}
}
So now I have a way to filter only the checked rows, and make sure only the LicenseHolderID which match those in the row with a checked CheckBox, are actually saved. I updated my main method:
if (LicenseHolderID == PreviewInvoiceViewModel.PreviewedInvoice) {
document.Save($"{fullPath}");
SourcePath = fullPath;
}
And I bound SourcePath to the the source of the WebView2 in the XAML.
I feel like this is a clunky way of doing it, and I am going back and forth between layers, as a comment (since removed) mentioned.
If anyone can show me a better way, I'm all ears..
Project table contains project list project has columns (Id, name, PM, etc. ) when clicking on a project, Project details page opens for the project. the Automation find the project and click on it but I get this error
OpenQA.Selenium.StaleElementReferenceException: The element reference
of is stale; either the element is no longer attached to the DOM,
it is not in the current frame context, or the document has been
refreshed
I think after clicking on the project, after going to the Proj detail page, the loop doesn't stop. how can I break; from all the loops after I find my project
I am new to automation I need help
public static void SelectProject()
{
IWebElement Table = Driver.Instance.FindElement(By.Id("projectsGrid"));
ReadOnlyCollection<IWebElement> allRows = Table.FindElements(By.TagName("tr"));
foreach (IWebElement row in allRows)
{
ReadOnlyCollection<IWebElement> cells = row.FindElements(By.TagName("td"));
foreach (IWebElement cell in cells)
{
if (cell.Text.Contains("002032"))
{
cell.Click();
break;
}
}
}
}
No need for flags, use return instead of break.
return;
I personally try to avoid break statements, so I would try and use a while or for with a sentinel value but if you want to use foreach you should include a bool that decides whether you break for each loop. Once you find what you want then set the bool. For example:
public static void SelectProject()
{
IWebElement Table = Driver.Instance.FindElement(By.Id("projectsGrid"));
ReadOnlyCollection<IWebElement> allRows =
Table.FindElements(By.TagName("tr"));
bool loop = true;
foreach (IWebElement row in allRows)
{
ReadOnlyCollection<IWebElement> cells =
row.FindElements(By.TagName("td"));
foreach (IWebElement cell in cells)
{
if (cell.Text.Contains("002032"))
{
cell.Click();
loop = false;
}
if (!loop)
break;
}
if (!loop)
break;
}
}
You can get rid of 99% of the logic in this function with a good locator.
public void SelectProject()
{
Driver.Instance.FindElement(By.XPath("[#id='projectsGrid']//td[.='002032']")).Click();
}
The above method with throw an exception if this element doesn't exist. Your current method won't do anything (throw an exception or click an element) if the element doesn't exist. If you want that behavior, use the code below.
public void SelectProject()
{
IReadOnlyCollection<IWebElement> e = Driver.Instance.FindElements(By.XPath("[#id='projectsGrid']//td[.='002032']"));
if (e.Any())
{
e.ElementAt(0).Click();
}
}
You can use a boolean flag called done. It is initially false and only get's set to true just before breaking from the inner loop. If you break out the inner loop, immediately after that in the outer loop check that done == true. If so, that means you broke out the inner loop, so you should also break out the outer loop.
public static void SelectProject()
{
// Boolean flag for when we break from inner loop
bool done = false;
IWebElement Table = Driver.Instance.FindElement(By.Id("projectsGrid"));
ReadOnlyCollection<IWebElement> allRows = Table.FindElements(By.TagName("tr"));
foreach (IWebElement row in allRows)
{
ReadOnlyCollection<IWebElement> cells = row.FindElements(By.TagName("td"));
foreach (IWebElement cell in cells)
{
if (cell.Text.Contains("002032"))
{
cell.Click();
done = true;
break;
}
}
// Check if the inner loop
// broke, if so also break here
if (done == true) {
break;
}
}
}
First time here. So I was wanting some help on a selenium testing that's got me stuck now for several hours.
So I stored a IList<IWebElement> on a page, composed of <a>. I was able to click the 1st element of that IList through a foreach, get what I need from the new page and go back to the page of the list using driver.manage.navigate.back().
However, this time, I can't click the 2nd element of the list.
Is there a way to find that 2nd element, 3rd element, 4th element, etc?
static void Main(string[] args)
{
IWebDriver driver = new FirefoxDriver();
string url = "http://DummyPageOne.Com";
driver.Navigate().GoToUrl(url);
driver.Manage().Timeouts().ImplicitlyWait(Timespan.FromSeconds(10));
PageOne page = new PageOne();
foreach (IWebElement item in page.Items)
{
item.Click();
ItemDetails details = new ItemDetails();
details.SaveImage(#"D:\Images\");
driver.Navigate().Back();
}
}
public class PageOne()
{
public PageOne()
{
PageFactory.InitElements(driver, this);
}
public IList<IWebElement> Items;
public void StoreItems()
{
string locator = "DummyLocator";
Items = driver.FindElements(By.Xpath(locator));
}
}
public class ItemDetails()
{
public ItemDetails()
{
PageFactory.InitElements(driver, this);
}
public void SaveImage()
{
}
}
On the basis of what I understood from your question, here is the way to achieve that. I checked the following C# Code on Chrome and it's working perfectly. I hope this helps.
[Test]
public void ClickAllLinks()
{
//Navigate to URL
driver.Navigate().GoToUrl(#"https://www.google.co.in/#q=Selenium");
//Store Links in IList
IList<IWebElement> resultLinks = driver.FindElements(By.CssSelector("div#ires>ol>div:not(:first-child)>div>div>h3>a"));
//Print Count
//Console.WriteLine(googleLinks.Count);
for (int i = 0; i < resultLinks.Count; i++)
{
int j = i + 1;
//Click on Link
driver.FindElement(By.CssSelector("div#ires>ol>div:not(:first-child)>div:nth-child(" + j + ")>div>h3>a")).Click();
//Print Element Locator
//Console.WriteLine("div#ires>ol>div:not(:first-child)>div:nth-child(" + j + ")>div>h3>a");
Thread.Sleep(2000); //Static wait is not recommended, add conditional wait
//Get what you need from new page
//Navigate back parent page
driver.Navigate().Back();
Thread.Sleep(2000); //Static wait is not recommended, add conditional waits
}
}
If this is not what you want please let me know.
The simple way is to initialize that list again in the end of loop.
Ok, the thing to remember about .Net and Selenium, is that the C# variables/types are references to things that exist in memory (on screen) in the browser.
When the browser window navigates away, the reference held by those variables will be invalid. You must 're-find' them.
I would be very surprised if you could click the first item in the list either.
The only way to click on these objects, is to reacquire the object after the page has reloaded.
Eg: Driver.FindElement(By.XPath("\\div[#class='somelocator']"));
If you can, I would recommend constructing a collection of 'By' classes.
Then loop through the By classes using them in the Find method.
That way, each iteration of the loop, you will be acquiring the object fresh.