Comparing of list's elements downloaded by WebDriver - list

I need to compare elements from tables which are presented on a few subpages (only values from first column).
Using lists I created one big list (which has ex. 95 elements). Unfortunately when I start comparing them, my test end with an error message "org.openqa.selenium.StaleElementReferenceException: stale element reference: element is not attached to the page document"
Everything works correctly when I have less than 10 elements (only one table on one page).
Do someone know what should I change in my code?
List<WebElement> pagesList = webDriver.findElements(By.cssSelector("#div_table > ul > li"));
if (pagesList.size() > 0) {
boolean nextPagePresent = true;
List<String> documentsList = webDriver.findElements(By.cssSelector("#table > tbody > tr > td:nth-child(1)"));
while (nextPagePresent == true) {
try {
nextPagePresent = webDriver.findElements(By.cssSelector("#div_table > ul > li.nextLink")).size() > 0;
documentsList += webDriver.findElements(By.cssSelector("#table > tbody > tr > td:nth-child(1)"));
if(nextPagePresent == true) {
webDriver.findElement(By.cssSelector("#div_table > ul > li.nextLink")).click();
}
} catch (org.openqa.selenium.NoSuchElementException ex) {
throw new Exception('Page not found');
}
}
documentsList.removeRange(0, 10);
for (int i = 0; i < documentsList.size(); i++) {
for (int k = i + 1; k < documentsList.size(); k++) {
if (documentsList.get(i).getText().equals(documentsList.get(k).getText())) {
log.info(documentsList.get(i).getText())
assert false;
}
}
}
} else {
List<String> documentsList = webDriver.findElements(By.cssSelector("#table > tbody > tr > td:nth-child(1)"));
for (int i = 0; i < documentsList.size(); i++) {
for (int k = i + 1; k < documentsList.size(); k++) {
if (documentsList.get(i).getText().equals(documentsList.get(k).getText())) {
assert false;
}
}
}
}

The exception you're seeing is expected since you have elements located on multiple pages and you're actually switching between them.
Basically the exception you're getting indicates that, i.e. it says it is unable to retrieve text from WebElement, which is logically correct since the element is no longer attached to the DOM model.
What you could do is to extract text from elements that are currently visible on the page and put it into separate List<String>, once you'll extract text from all the elements from all the pages you could compare it.
So order of actions should look like this:
Load page
Extract text from all visible elements and put it into separate list
Change page
Repeat steps 2,3 until there are no pages left
Compare results
More detailed info on exception could be found here: https://www.seleniumhq.org/exceptions/stale_element_reference.jsp
P.S. Usage of build-in asserts should be avoided, consider using test java test libraries like jUnit or TestNg or assert specific libraries like assert4j it would give you more flexibility.

Related

CouchDB view reduce one doc per key

I'm trying to solve what seems like a fairly simple problem with a couchDb view, but I'm not even getting close to the target with my result set.
Rather than updating documents, I'm creating a new document every time as my versioning strategy, and tying those documents together with a versioning field called ver. The very first document in a version chain will see the ver field and the _id field having the same value. All subsequent documents in the chain will have the same ver field as previous docs in the chain, but will have a unique _id field. These documents also have a createdTime field which is my way of knowing which document is the latest.
Here's my documents:
{
"_id": "abcd-1234-efgh-9876",
"ver": "abcd-1234-efgh-9876",
"createdTime": "2020-01-12 01:15:00 PM -0600",
...
},
{
"_id": "uopa-3849-pmdi-1935",
"ver": "abcd-1234-efgh-9876",
"createdTime": "2020-02-16 02:39:00 PM -0600",
...
}
Here's my map function:
function (doc) {
emit(doc.ver, doc);
}
Here's my reduce function:
function(keys, values, rereduce) {
var latestVersions = {};
for (var i = 0; i < keys.length; i++) {
var found = latestVersions[keys[i][0]];
if (!found || found.createdTime < values[i].createdTime) {
latestVersions[keys[i][0]] = values[i];
}
}
return latestVersions;
}
And finally, here's my desired output from the view (just the doc that I want):
{
"_id": "uopa-3849-pmdi-1935",
"ver": "abcd-1234-efgh-9876",
"createdTime": "2020-02-16 02:39:00 PM -0600",
...
}
What am I missing here? The reduce function is returning both records, which is not what I want. Is what I'm trying to achieve possible or is there a better way to go about this?
Update
I was able to get this to work when a single key is used to access the view, which is one of my use cases.
function (keys, values, rereduce) {
var toReturn = values[0];
for (var i = 1; i < values.length; i++) {
if (values[i].createdTime > toReturn.createdTime) {
toReturn = values[i];
}
}
return toReturn;
}
I have another use case that will be returning all of the data in the view, however. The desired result there is the same as above, but the function I'm using for single keys will only ever return one result. How do I filter multiple values with a shared key such that 1 "shared" key:n values -> 1 key:1 value.
I was finally able to resolve this when I stumbled upon this couchbase article. It was much more articulate than some of the other dry computer-science documentation.
I still do not understand why certain items are grouped in a reduce method and other ones are not. For example, reduce was called 5 times for 6 items that shared an identical key; only one of the keys had actually grouped anything -- an array of two documents. It probably has something to do with those dry computer-science B-tree documents I glossed over.
Anyway, I was able to determine that all I needed to do was group the values by the ver field in both scenarios (the only difference being that rereduce had a 2 dimensional array). Here's what my reduce function ended up looking like:
function (keys, values, rereduce) {
var toValues = function(myMap) {
return Object.keys(myMap).map(function(key) {
return myMap[key];
});
}
if (rereduce) {
// values should look like [[{...}, {...}], [{...}]]
var outputMap = {};
for (var i = 0; i < values.length; i++) {
for (var j = 0; j < values[i].length; j++) {
var currentEl = values[i][j];
var found = outputMap[currentEl.ver];
if ((found && found.createdDate < currentEl.createdDate) || !found) {
outputMap[currentEl.ver] = currentEl;
}
}
}
return toValues(outputMap);
} else {
var outputMap = {};
for (var i = 0; i < values.length; i++) {
var found = outputMap[values[i].ver];
if ((found && found.createdDate < values[i].createdDate) || !found) {
outputMap[values[i].ver] = values[i];
}
}
return toValues(outputMap);
}
}

multiple lists into a map

I had a question regarding my code below. I'm reading a file containing lots of data of which some stuff is irrelevant. The data is written out on one line, so I cannot use nextLine or something.
For each vertex, I save the relevant information into dataperpoint. When I go to the next vertex, I want to clear the list to fill it with new relevant information.
The issue that I have is that each time I clear dataperpoint, all values in Map get cleared. When I then try to fill it, all previous positions in the Map get the same values.
How can I do this and make sure that each vertex will get his own list?
Looking forward to your suggestions!
public static Map<Integer, List<Double>> readData(File f) // throws IO exception?
{
// Create Map to store the vertex and list with relevant information in
List<Double> dataperpoint = new ArrayList<Double>();
Map<Integer, List<Double>> data = new HashMap<>();
// Open the scanner
try (Scanner in = new Scanner(f))
{
// To make sure the correct localization is used
in.useLocale(Locale.US);
// The first six integers contain irrelevant information
for (int step = 1; step <= 6; step++)
{
in.nextInt();
}
// Do the information for vertex 0 separately, since it has one data point less
int vertex = in.nextInt();
for (int doubleinfo = 1; doubleinfo <= 4; doubleinfo++) // six relevant variables
{
dataperpoint.add(in.nextDouble());
}
// irrelevant information
for (int irrelevantinfo = 1; irrelevantinfo <= 2; irrelevantinfo++)
{
in.nextInt();
}
// Opening and Closing of time window
dataperpoint.add((double) in.nextInt());
dataperpoint.add((double) in.nextInt());
data.put(vertex, dataperpoint);
while (in.hasNext()) // think of different statement later
{
dataperpoint = new ArrayList<Double>();
vertex = in.nextInt();
for (int doubleinfo = 1; doubleinfo <= 4; doubleinfo++) // six relevant variables
{
dataperpoint.add(in.nextDouble());
}
// irrelevant information
for (int irrelevantinfo = 1; irrelevantinfo <= 3; irrelevantinfo++)
{
in.nextInt();
}
// Opening and Closing of time window
dataperpoint.add((double) in.nextInt());
dataperpoint.add((double) in.nextInt());
data.put(vertex, dataperpoint);
}
in.close();
}
catch (FileNotFoundException e)
{
e.printStackTrace();
}
Use LinkedHashMap<> instead of HashMap<> it should solve your problem. Read this Difference between HashMap, LinkedHashMap and TreeMap

Sidebar to show currently displayed markers - combined IF statement

I would really appreciate some help with this problem. It may be easy to solve but I just can't figure out how to go about coding it. I have a map based on this Geocodezip Example Categories which displays markers and has a sidebar next to it. The markers belong to one of three categories and based on whether the checkboxes are checked, the markers will appear on the map.
function show(category) {
for (var i=0; i<gmarkers.length; i++) {
if (gmarkers[i].mycategory == category) {
gmarkers[i].setVisible(true);
}
}
document.getElementById(category+"box").checked = true;
}
function hide(category) {
for (var i=0; i<gmarkers.length; i++) {
if (gmarkers[i].mycategory == category) {
gmarkers[i].setVisible(false);
}
}
document.getElementById(category+"box").checked = false;
infowindow.close();
}
At the same time, I am trying to display the markers' info in the sidebar but under two conditions:
the marker category must be checked &
the marker must be contained in the map viewport/bounds
I then added one code for the sidebar that only displays the markers for checked categories and one where the info is shown in the sidebar when the markers are in the viewport. However, I can only do these separately (for to completely different codes) and cannot get both conditions to work together. These are the two examples that I am trying to merge:
Include in sidebar if box is checked
function makeSidebar() {
var side_bar_html = "";
for (var i=0; i<gmarkers.length; i++) {
if (gmarkers[i].getVisible()) {
side_bar_html += '<a href="javascript:myclick(' + i + ')">' + gmarkers[i].myname + '<\/a><br>';
}
}
document.getElementById("side_bar").innerHTML = side_bar_html;
}
(when bounds change) Include in sidebar if marker is contained within bounds
function makeSidebar() {
google.maps.event.addListener(map, 'bounds_changed', function() {
var side_bar_html = "";
var bounds = map.getBounds();
for (var i=0; i<gmarkers.length; i++) {
if (bounds.contains(gmarkers[i].position)) {
side_bar_html += '<a href="javascript:myclick(' + i + ')">' + gmarkers[i].myname + '<\/a><br>';
}
}
document.getElementById("side_bar").innerHTML = side_bar_html;
});
}
I tried putting the two together in a combined if statement (&&) within the if bounds.contains statement but the sidebar won't change if a box is checked/unchecked.
Add the check to makeSidebar only when the map bounds is available:
// == rebuilds the sidebar to match the markers currently displayed ==
function makeSidebar() {
var html = "";
for (var i=0; i<gmarkers.length; i++) {
if (gmarkers[i].getVisible() &&
map.getBounds &&
map.getBounds() &&
map.getBounds().contains(gmarkers[i].getPosition())) {
html += '<a href="javascript:myclick(' + i + ')">' + gmarkers[i].myname + '<\/a><br>';
}
}
document.getElementById("side_bar").innerHTML = html;
}
execute makeSidebar when the bounds_changed event fires:
google.maps.event.addListener(map, 'bounds_changed', makeSidebar);
working fiddle

Multidimensional vector of class type

i would need a little help with vector of vector when type is class. "loadTruck" is my class. I want to store values from my class "loadTruck" into 2D vector in this order: vector(truck ,print). Both "truck" and "print" are type of class "loadTruck". I want acces to this vector: for example lets say that "print" is package and "truck" is truck. I want to get information about package in truck 1, with package id 20. I am absolutely confused with this.
vector<vector <loadTruck>> jedinecTruck;
loadTruck truck;
while (warehouseIsEmpty == False)
{
truck.resetTruck();
for (int i = 0; i < numOfPackages; i++)
{
vector<loadTruck> print;
if (store2D[i].checkIfPackageIsThere() == true)
{
truck.setPackageSize(store2D[i].getPackageWidth(), store2D[i].getPackageHeight());
if (truck.checkIfPackageCanBeLoaded() == true)
{
print.push_back(truck);
truck.loadPackageInTruck();
store2D[i].deletePackage();
}
}
}
jedinecTruck.push_back(truck);
}
Version with 3D array,\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
now programm is running well, but array is not good idea but i hope that it will help you to understand what i want to do. I need to use vector because i am not able to define first 2 elements of array, it depends on algorythm and is always changing. For this example i suppose that first two elements will be defined statically. 3 rd element got always 4 components.
vector<loadTruck> jedinecTruck;
loadTruck truck;
int array[1][3][4];
while (wharehouseIsEmpty == false)
{
truck.resetTruck();
for (int i = 0; i < numOfPackages; i++)
{
if (store2D[i].checkIfPackageIsThere() == true)
{
truck.setPackageSize(store2D[i].getPackageWidth(), store2D[i].getPackageHeight());
if (truck.checkIfPackageCanBeLoaded() == true)
{
truck.loadPackageInTruck();
store2D[i].deletePackage();
array[0][i][0] = truck.getActualX1();
array[0][i][1] = truck.getActualX2();
array[0][i][2] = truck.getActualY1();
array[0][i][3] = truck.getActualY2();
}
}
}
jedinecTruck.push_back(truck);
}
Regards
Perhaps this is closer to what you're trying to do:
vector<vector <loadTruck>> jedinecTruck;
loadTruck truck;
while (warehouseIsEmpty == False)
{
truck.resetTruck();
vector<loadTruck> print;
for (int i = 0; i < numOfPackages; i++)
{
if (store2D[i].checkIfPackageIsThere() == true)
{
truck.setPackageSize(store2D[i].getPackageWidth(), store2D[i].getPackageHeight());
if (truck.checkIfPackageCanBeLoaded() == true)
{
print.push_back(truck);
truck.loadPackageInTruck();
store2D[i].deletePackage();
}
}
}
jedinecTruck.push_back(print);
}
It's difficult to tell from your question, though.

Day(s) of week selection from QT checkbox to Postgresql

I have a group of checkboxes representing the days of a week in my Qt application GUI and I select one or many days, and depending on which boxes are checked, pass a query string to PostgreSQL in order to display certain data on those days -e.g. if I checked monday and wednesday, extract (dow from timestamp) = 1 or extract(dow from timestamp) = 3 should be added to my query. I have just typed a crude solution -though haven't tested yet as I write this-, but I was wondering if there is a shorter -and more elegant- approach that I'm missing out here. The code is as below: -the queryAdditionCalltimePart and queryAdditionCallStampPart strings are later added to the relevant parts of my main query's QString before the main query is executed-
bool checkboxArray[7];
bool mult = false;
checkboxArray[0] = this->ui->checkBoxMonday->isChecked();
checkboxArray[1] = this->ui->checkBoxTuesday->isChecked();
checkboxArray[2] = this->ui->checkBoxWednesday->isChecked();
checkboxArray[3] = this->ui->checkBoxThursday->isChecked();
checkboxArray[4] = this->ui->checkBoxFriday->isChecked();
checkboxArray[5] = this->ui->checkBoxSaturday->isChecked();
checkboxArray[6] = this->ui->checkBoxSunday->isChecked();
QString queryAdditionCalltimePart = "";
QString queryAdditionCalStampPart = "";
int count = 0;
queryAdditionCalltimePart.append("(");
queryAdditionCalStampPart.append("(");
for(int i = 0; i < 7; i++)
{
if(checkboxArray[i] == true)
{
count++;
}
}
int x = 0;
for(int i = 0; i < 7; i++)
{
if(checkboxArray[i] == true)
{
queryAdditionCalltimePart.append("(SELECT EXTRACT(DOW FROM calltime) = '" +QString::number(i+1)+"')");
queryAdditionCalStampPart.append("(SELECT EXTRACT(DOW FROM cal.stamp) = '" +QString::number(i+1)+"')");
}
if(count > 1 && checkboxArray[i] == true)
{
if(x == count - 1)
{
}
else
{
queryAdditionCalltimePart.append("OR");
queryAdditionCalStampPart.append("OR");
x++;
}
}
}
queryAdditionCalltimePart.append(")");
queryAdditionCalStampPart.append(")");
You can add properties to any widget in Qt, http://qt-project.org/doc/qt-4.8/qobject.html#setProperty. The property can have any information that you want.
In your particular case, it would be cleaner to attach the SQL string as a property for each checkbox.
this->ui->checkBoxMonday->setProperty("sql",
"(SELECT EXTRACT(DOW FROM calltime) = '" +QString::number(i+1)+"') OR ";
Once you receive the user input, simply append the check box properties and remove the final OR.