So my issue is as follows, I am attempting to check all elements on the page and verify element.GetAttribute("class") == expectedClass. Here is the code
var feedback = Driver.FindElements(AuctivaSalesPageModel.ViewFeedbackSelector);
var attempts = 0;
foreach (IWebElement element in feedback)
{
while (attempts < 3)
{
try
{
Assert.AreEqual("leaveFeed actionTaken", element.GetAttribute("class"));
attempts = 0;
break;
}
catch (StaleElementReferenceException)
{
Assert.AreEqual("leaveFeed actionTaken", element.GetAttribute("class"));
attempts = 0;
break;
}
catch (AssertionException)
{
System.Threading.Thread.Sleep(3000);
Driver.Navigate().Refresh();
AuctivaSalesPage.WaitForElementVisible(Driver, AuctivaSalesPageModel.TotalNumberOfSalesSelector);
AuctivaSalesPage.ScrollToTop();
AuctivaSalesPage.SelectNoFolder();
attempts++;
}
}
}
Now I have been reading up on the StaleElementException and I think that my catch and retry approach is useless as if the DOM has refreshed then the element within the list will always be stale. I believe what I need to do here is refind the element with a Driver.FindElement() but being that I am encountering this issue within a foreach loop of IWebElements I am not sure how to get the selector for the specific element that is failing to retry?
Should I catch the exception rebuild the list and then retry the whole foreach loop? or is there a way to extract the selector specific to the element within the loop so I can do something along the lines of
Assert.AreEqual("leaveFeed actionTaken", Driver.FindElement(By.someSelector(element.GetSelector)).GetAttribute("class"));
I hope this helps, but i had a similar problem and was able to get around it using the following logic, granted its not the best approach but it works:
var feedbackCount = Driver.FindElements(AuctivaSalesPageModel.ViewFeedbackSelector).Count();
var attempts = 0;
for(var i = 0; i < feedbackCount; i++)
{
while (attempts < 3)
{
var element = Driver.FindElements(AuctivaSalesPageModel.ViewFeedbackSelector).ElementAt(i);
//Continue you logic here
}
}
Hope this help
Related
So, I want to store the inventory of the player in Firebase Realtime Database.
I set up a basic schema, and I want to add the items (their names, actually) under the inventory "branch".
I am trying to do it with the push method, but it ain't working for me right now.
I got the following script which updates the player's inventory client-side and should update the database to, with appending the inventory "branch".
What am I doing wrong?
public void pickUp() {
Transform free_place = findFirstFreePlace();
if(free_place) {
for (int i = 0; i < items.Length; i++) {
if (free_place.name.Contains(i.ToString())) {
items[i] = currentPickable;
reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory").Child(items[i].item_name).Push();
lastIndex = i;
}
}
free_place.GetChild(0).GetChild(0).GetComponent<Image>().sprite = items[lastIndex].item_pic;
free_place.GetChild(0).GetChild(0).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Button>().interactable = true;
}
}
I'm guessing that there's slight confusion around what Push() does in this context. If you read the documentation, it says:
Add to a list of data. Every time you call Push(), Firebase generates a unique key that can also be used as a unique identifier, such as user-scores/<user-id>/<unique-score-id>.
Basically, it's giving you a place you can safely write into rather than actually adding something to the Database. You should check out the sample, but I think I can illustrate both what you intend to do and a cool way to use Push() for your use case.
public async void pickUp() {
Transform free_place = findFirstFreePlace();
if(free_place) {
int lastIndex = 0;
var elements = new List<string>();
for (int i = 0; i < items.Length; i++) {
if (free_place.name.Contains(i.ToString())) {
items[i] = currentPickable;
elements.Add(items[i].item_name);
lastIndex = i;
}
}
await reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory").SetValueAsync(itemNames);
free_place.GetChild(0).GetChild(0).GetComponent<Image>().sprite = items[lastIndex].item_pic;
free_place.GetChild(0).GetChild(0).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Button>().interactable = true;
}
}
What I'm doing here is that I build a list of the items before hand. Then I set the inventory entry to this list. If I depended on the previous state, I'd probably prefer to do this as a transaction instead.
I also use async/await. I'm not sure how important it is that this logic runs before you update your game's UI, but setting anything in Firebase is an asynchronous operation (it runs in the background). You can omit this if it's not important that you stay in sync.
If you wanted to have uniquely identified weapon slots. You could instead do something like:
var childReference = reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory");
var key = childReference.Key;
// ... more logic here ...
childReference.Child(key).SetValueAsync(item_name);
A few more hints:
I'd recommend that you use the ValueChanged listener whenever possible to keep your game state in sync with your server state. Realtime Database will always be running asynchronously in the background, and this way you can help stay up to date as more data becomes available.
If asynchronous programming in Unity is new to you, check out my post on the subject.
Okay, so I've finally figured it out.
With the help of Push() method's unique id generation, I am able to add childs with the same item name to the inventory node.
Firstly, I create that unique id, then, I add that unique id to the database and set it's value to the item name.
Edit: I mixed my original solution with Pux0r3's solution, so that it uses async/await.
public async void pickUp() {
Transform free_place = findFirstFreePlace();
if (free_place) {
int lastIndex = 0;
var elements = new List<string>();
for (int i = 0; i < items.Length; i++) {
if (free_place.name.Contains(i.ToString())) {
items[i] = currentPickable;
elements.Add(items[i].item_name);
lastIndex = i;
}
}
string key = reference.Child(auth.CurrentUser.UserId).Child("inventory").Push().Key;
await reference.Child("users").Child(auth.CurrentUser.UserId).Child("inventory").Child(key).SetValueAsync(currentPickable.item_name);
free_place.GetChild(0).GetChild(0).GetComponent<Image>().sprite = items[lastIndex].item_pic;
free_place.GetChild(0).GetChild(0).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Image>().enabled = true;
free_place.GetChild(1).GetComponent<Button>().interactable = true;
}
}
I have block of code which contains a lot of nested loops (I have to mention over here is that we don't have a problem with speed of the code). Now in turn on that I have to have the same functionality within the same Controller Action with all the loops again, but I don't want to just copy and paste the same code and just changing the variables over there.
The idea of the code is with the first For loop we are making requests over some API and if the response is empty we are stop sending request. Inside the loop we are reading JSON response of every calls and add it to our class:
var transactions = new List<Transaction>();
for (int i = 0; i < pages; i++)
{
if (morePagesInXero == false)
{
valueCollection.Add("page", pages.ToString());
morePages= true;
}
else
{
valueCollection.Set("page", pages.ToString());
}
var invoices = api.GetArray(GlobalConstants.Invoices, where, null, valueCollection);
dynamic invoicesResponse=JObject.Parse(invoices.ToString());
var jsonTransactions =((JProperty((JContainer)invoicesResponse).Last).Value;
var arrayTransactions = ((JArray)jsonTransactions);
// Check if there are more than 1 page
if (arrayTransactions.Count == 0)
{
break;
}
else
{
foreach (var item in jsonTransactions)
{
transaction.taxDirection = "DummyDataFromResponseJSON";
transaction.invoiceNumber = "DummyData1FromResponseJSON"
transaction.upstreamModifiedAt = "DummyData2FromResponseJSON";
transaction.currency = "DummyData3FromResponseJSON";
transaction.customerId = "DummyData4FromResponseJSON";
// and so on ...
transactions.Add(transaction);
}
pages++;
}
Now I have to make a new call to the API on different end point but the logic of manipulating the JSON response is the same one with those loops. Please advice is it possible to reuse the code above in order to make it more generic and just reuse it.
Thanks in advance.
Problem code:
for (int i = 0; i <= numberOfPlayers - 1; i++)
{
if (i == dealerPosition())
{
StringBuilder sb = new StringBuilder();
// e.g. outputs "tbPosition1"
sb.Append("tbPosition").Append(dealerPosition().ToString());
// The following line of code does not work as sb is a string containing
// "thPosition1", not my controller tbPosition1. How do I fix this?
Dispatcher.Invoke(() => { (sb.Text = dealerPosition().ToString(); });
break;
}
}
Using C#, WPF, Visual Studio.
sb.Append("tbPosition").Append(dealerPosition().ToString());
// The following line of code does not work as sb is a string containing
// "thPosition1", not my controller tbPosition1. How do I fix this?
Dispatcher.Invoke(() => { ((this.FindName(sb.ToString()) as TextBox).Text = dealerPosition().ToString(); });
From Complexity's comment though, the post mentions that you can add all of your elements into the list and loop / foreach that when you want to work on it:
List<TextBox> textBoxesToEdit = new List<TextBox>(){tbposition1, tbposition2 /*so on*/};
foreach (TextBox textbox in textBoxesToEdit)
{
//do stuff
}
FindName() should help you with this, but note that you may have to may have to register each control as you create it if you add them after the control is initially created.
In line with your answer to my quetion I would store my tdPositionXYZ in an array or List and do this instead of all of your code:
Dispatcher.Invoke(() => {
tbPositionArray[dealerPosition()].Text = dealerPosition().ToString();
});
I have made automated test with selenium c# and have a probelm. My test writes some info in form and then submits, if after submiting div that contains some info has info "Formoje yra klaidu", it must write to file email from form, but the problem is that this div is not visible when email isn't wrong and my test just stops on place where Iwebelement finds element by xpath because the element isn't visible. Here's some of the code
for (int i = 0; i < array.Length; i++)
{
IWebElement PasirinktiParkinga = driver.FindElement(By.CssSelector("#zone_16 > td:nth-child(5) > a:nth-child(1)"));
PasirinktiParkinga.Click();
IWebElement Vardas = driver.FindElement(By.Id("firstname1"));
Vardas.Clear();
Vardas.SendKeys("Vardas");
IWebElement Pavarde = driver.FindElement(By.Id("lastname1"));
Pavarde.Clear();
Pavarde.SendKeys("Pavarde");
IWebElement AutoNumeris = driver.FindElement(By.Id("vehicle_number1"));
AutoNumeris.Clear();
AutoNumeris.SendKeys("ASD123");
IWebElement Pastas = driver.FindElement(By.Id("email1"));
Pastas.Clear();
Pastas.SendKeys(array[i]);
IWebElement Taisykles = driver.FindElement(By.CssSelector("div.checks:nth-child(5) > div:nth-child(1) > label:nth-child(2)"));
Taisykles.Click();
IWebElement uzsakyti = driver.FindElement(By.CssSelector(".submit-zone > input:nth-child(1)"));
uzsakyti.Click();
System.Threading.Thread.Sleep(TimeSpan.FromSeconds(5));
IWebElement MessageRed = driver.FindElement(By.XPath("//*[#id='step_2']/div[3]")); //This line is were i wan't to find this div but i must write it so that if there isn't there - just do the for cicle
if (MessageRed.Text.Contains("Formoje yra klaidų."))
{
failure += array[i] + "\n";
System.IO.File.WriteAllText(#"C:\Users\jarek\Desktop\Failureemail\failure.txt", failure);
}
IWebElement unipark = driver.FindElement(By.CssSelector(".logo > a:nth-child(1)"));
unipark.Click();
i++;
}
How to make that if this element isn't there, code don't stop.
Can any body help me ???
Well, first of all don't use any Thread.Sleeps at all. Use Implicit and Explicit waits instead. Secondly, try to not use xpath (very difficult to maintain, understand it). And if you need to verify elements existance you can do it in next way e.g.
var elements = driver.FindElements(By.XPath("//*[#id='step_2']/div[3]"));
if(elements.Count() > 0)
// do everything you want
else
//continue doing smth
or you can try catch ElementNotFound exception... it's all depends.
You should check to see if the element exists, in this case check and see if the size of the element is greater than 0. This is how I could do it in Java:
if (driver.FindElement(By.XPath("//*[#id='step_2']/div[3]")).size() > 0)
{
//perform your action now
}
else
{
//perform action if the element is not present
}
I did it like this and it worked
if (driver.FindElements(By.XPath("//*[#id='step_2']/div[3]")).Count != 0)
Be carefull with FindElements, the test can be very long to execute if you have huge pages.
When I must use the FindElements to search an element, I use a FindElement that can help me to scope where I must find the researched element with FindElements. In my case, my executing time is reduced of 2 seconds everytime I use directly FindElements
Use an Implicit Wait. This allows you to enter a value in seconds that webdriver will wait for an element if it isn't found initially. This example is set for 2 seconds.
driver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(2)
You could also use a try{} catch{}.
Also if you want to clean up your code you could write functions for finding elements and then just pass that id name into the function. It will make things a lot clearer and easier to read.
Here is my method for finding an element by ID
static void ClickElement_ByID(string elementName)
{
try
{
IWebElement test = driver.FindElement(By.Id(""+elementName+""));
Console.WriteLine("Found: "+elementName);
test.Click();
}
catch (Exception e)
{
Console.WriteLine(e);
}
I have written a function to update Default Price List for all the Active Products on the CRM 2013 Online.
//The method takes IOrganization service and total number of records to be created as input
private void UpdateMultipleProducts(IOrganizationService service, int batchSize, EntityCollection UpdateProductsCollection, Guid PriceListGuid)
{
//To execute the request we have to add the Microsoft.Xrm.Sdk of the latest SDK as reference
ExecuteMultipleRequest req = new ExecuteMultipleRequest();
req.Requests = new OrganizationRequestCollection();
req.Settings = new ExecuteMultipleSettings();
req.Settings.ContinueOnError = true;
req.Settings.ReturnResponses = true;
try
{
foreach (var entity in UpdateProductsCollection.Entities)
{
UpdateRequest updateRequest = new UpdateRequest { Target = entity };
entity.Attributes["pricelevelid"] = new EntityReference("pricelevel", PriceListGuid);
req.Requests.Add(updateRequest);
}
var res = service.Execute(req) as ExecuteMultipleResponse; //Execute the collection of requests
}
//If the BatchSize exceeds 1000 fault will be thrown.In the catch block divide the records into batchable records and create
catch (FaultException<OrganizationServiceFault> fault)
{
if (fault.Detail.ErrorDetails.Contains("MaxBatchSize"))
{
var allowedBatchSize = Convert.ToInt32(fault.Detail.ErrorDetails["MaxBatchSize"]);
int remainingCreates = batchSize;
while (remainingCreates > 0)
{
var recordsToCreate = Math.Min(remainingCreates, allowedBatchSize);
UpdateMultipleProducts(service, recordsToCreate, UpdateProductsCollection, PriceListGuid);
remainingCreates -= recordsToCreate;
}
}
}
}
Code Description : There are around 5000 active product records in the System. So I am updating Default Price List for all of them using above code.
But, I am missing here something so that, it has updated only 438 records. It loops through the While statement correctly, but it is not updating all of them here.
What should be the Batchsize when we run this function for the First Time?
Any one can help me here?
Thank you,
Mittal.
You pass remainingCreates as the batchSize parameter but your code never references batchSize so you are just going to reenter that while loop every time.
Also, I'm not sure how you are doing all your error handling but you need to update your catch block so that it doesn't just let FaultExceptions pass-through if they don't contain a MaxBatchSize value. Right now, if you take a FaultException regarding something other than batch size it will be ignored.
{
if (fault.Detail.ErrorDetails.Contains("MaxBatchSize"))
{
var allowedBatchSize = Convert.ToInt32(fault.Detail.ErrorDetails["MaxBatchSize"]);
int remainingCreates = batchSize;
while (remainingCreates > 0)
{
var recordsToCreate = Math.Min(remainingCreates, allowedBatchSize);
UpdateMultipleProducts(service, recordsToCreate, UpdateProductsCollection, PriceListGuid);
remainingCreates -= recordsToCreate;
}
}
else throw;
}
Instead of reactive handling, i prefer proactive handling of the MaxBatchSize, this is true when you already know what is MaxMatchSize is.
Following is sample code, here while adding OrgRequest to collection i keep count of batch and when it exceeds I call Execute and reset the collection to take fresh batch.
foreach (DataRow dr in statusTable.Rows)
{
Entity updEntity = new Entity("ABZ_NBA");
updEntity["ABZ_NBAid"] = query.ToList().Where(a => a.NotificationNumber == dr["QNMUM"].ToString()).FirstOrDefault().TroubleTicketId;
//updEntity["ABZ_makerfccall"] = false;
updEntity["ABZ_rfccall"] = null;
updEntity[cNBAttribute.Key] = dr["test"];
req.Requests.Add(new UpdateRequest() { Target = updEntity });
if (req.Requests.Count == 1000)
{
responseWithResults = (ExecuteMultipleResponse)_orgSvc.Execute(req);
req.Requests = new OrganizationRequestCollection();
}
}
if (req.Requests.Count > 0)
{
responseWithResults = (ExecuteMultipleResponse)_orgSvc.Execute(req);
}