I am writing an automated test for a dynamic webtable using Selenium Webdriver with chromedriver and testNG.
The objective is to assert that a certain table-entry is there, delete it if it is, and then assert if it is deleted. This second assert is not working properly however.
During the first assert I call the method that creates a list of Webelements and gets the number of rows. I use this number to know when to stop iterating through the table.
The second assert uses the same table to do the same thing, but now the DOM has changed, en there are only 18 rules left in my table were there were 19 before. As soon as the iteration tries to get the 19th row I get the following:
org.openqa.selenium.NoSuchElementException: no such element: Unable to
locate element: {"method":"xpath","selector":".//tr[19]/td[1]"}
I have tried creating a new instance of the MyWishlistPage, but this new instance also sees the "old" number of table rules
I have also tried a thread sleep and a driver refresh after the row delete, but this doesn't help either (piece of code is still there, commented)
I ended up altering my test class to go to another page and then return to the MYWishlistPage. This works, but it's a sloppy workaround that I'm not happy with
Can anyone tell me how I can get the correct number of rows after deleting an entry from the table?
This is a piece of the class for the page that I have the problem with :
public class MyWishlistsPage
{
private WebDriver driver;
public MyWishlistsPage(WebDriver driver)
{
this.driver = driver;
//This call sets the WebElements
PageFactory.initElements(driver, this);
}
public Boolean isWishlistAvailable(String nameToAssert)
{
//This list gets the number of rows from the table
List<WebElement> rows = driver.findElements(By.xpath(".//tr"));
//This loop finds the first row which' title matches sRowValue
for (int i = 1; i < rows.size(); i++)
{
String sValue = driver.findElement(By.xpath(".//tr[" + i + "]/td[1]")).getText();
if (sValue.equalsIgnoreCase(nameToAssert))
{
return true;
}
}
return false;
}
public void deleteWishlistsEntry (String sRowValue)
{
//This list gets the number of rows from the table
List<WebElement> rows = driver.findElements(By.xpath(".//tr"));
//This loop finds the first row which' title matches sRowValue
for (int i = 1; i < rows.size(); i++)
{
String sValue = driver.findElement(By.xpath(".//tr[" + i + "]/td[1]")).getText();
if (sValue.equalsIgnoreCase(sRowValue))
{
// If the sValue matches with the description, the element in the seventh column of the row will be clicked
driver.findElement(By.xpath(".//tr[" + i + "]/td[7]/a/i")).click();
driver.switchTo().alert().accept();
/* try
{
Thread.sleep(10000);
} catch(InterruptedException ex)
{
Thread.currentThread().interrupt();
}
driver.navigate().refresh(); */
}
}
}
}
This is a piece of the testclass I am calling the page from :
Assertions.assertThat(mywishlistspage.isWishlistAvailable(listToAssert)).as("The list you were trying to delete did not exist, and an attempt to create it failed ").isTrue();
//Deletes the chosen list
mywishlistspage.deleteWishlistsEntry(listToAssert);
//Verifies that list has been deleted
//homepage.clickMyAccountPage();
//myaccountpage.goToMyWishlistsPage();
Assertions.assertThat(mywishlistspage.isWishlistAvailable(listToAssert)).as("The list you tried to delete is still there").isFalse();
In the deleteWishlistsEntry, First it get the number of rows, after clicking on the "anchor" the line item will be deleted, after that u have to break / exit from that loop once it clicked on the anchor tag... But in the code u r looping until the 19th element that is why u get "Unable to locate element: {"method":"xpath","selector":".//tr[19]/td[1]"}"
public void deleteWishlistsEntry (String sRowValue)
{
//This list gets the number of rows from the table
List<WebElement> rows = driver.findElements(By.xpath(".//tr"));
//This loop finds the first row which' title matches sRowValue
for (int i = 1; i < rows.size(); i++)
{
String sValue = driver.findElement(By.xpath(".//tr[" + i + "]/td[1]")).getText();
if (sValue.equalsIgnoreCase(sRowValue))
{
// If the sValue matches with the description, the element in the seventh column of the row will be clicked
driver.findElement(By.xpath(".//tr[" + i + "]/td[7]/a/i")).click();
driver.switchTo().alert().accept();
break;
}
}
}
Related
I am trying to write a test for a before trigger that takes fields from a custom object and concatenates them into a custom Key__c field.
The trigger works in the Sandbox and now I am trying to get it into production. However, whenever I try and do a System.assert/assertEquals after I create a purchase and perform DML, the value of Key__c always returns null. I am aware I can create a flow/process to do this, but I am trying to solve this with code for my own edification. How can I get the fields to concatenate and return properly in the test? (the commented out asserts are what I have tried so far, and have failed when run)
trigger Composite_Key on Purchases__c (before insert, before update) {
if(Trigger.isBefore)
{
for(Purchases__c purchase : trigger.new)
{
String eventName = String.isBlank(purchase.Event_name__c)?'':purchase.Event_name__c+'-';
String section = String.isBlank(purchase.section__c)?'':purchase.section__c+'-';
String row = String.isBlank(purchase.row__c)?'':purchase.row__c+'-';
String seat = String.isBlank(String.valueOf(purchase.seat__c))?'':String.valueOf(purchase.seat__c)+'-';
String numseats = String.isBlank(String.valueOf(purchase.number_of_seats__c))?'':String.valueOf(purchase.number_of_seats__c)+'-';
String adddatetime = String.isBlank(String.valueOf(purchase.add_datetime__c))?'':String.valueOf(purchase.add_datetime__c);
purchase.Key__c = eventName + section + row + seat + numseats + adddatetime;
}
}
}
#isTest
public class CompositeKeyTest {
public static testMethod void testPurchase() {
//create a purchase to fire the trigger
Purchases__c purchase = new Purchases__c(Event_name__c = 'test', section__c='test',row__c='test', seat__c=1.0,number_of_seats__c='test',add_datetime__c='test');
Insert purchase;
//System.assert(purchases__c.Key__c.getDescribe().getName() == 'testesttest1testtest');
//System.assertEquals('testtesttest1.0testtest',purchase.Key__c);
}
static testMethod void testbulkPurchase(){
List<Purchases__c> purchaseList = new List<Purchases__c>();
for(integer i=0 ; i < 10; i++)
{
Purchases__c purchaserec = new Purchases__c(Event_name__c = 'test', section__c='test',row__c='test', seat__c= i+1.0 ,number_of_seats__c='test',add_datetime__c='test');
purchaseList.add(purchaserec);
}
insert purchaseList;
//System.assertEquals('testtesttest5testtest',purchaseList[4].Key__c,'Key is not Valid');
}
}
You need to requery the records after inserting them to get the updated data from the triggers/database
I'm testing a website of Stocks
I have a certain 'clock' in each stock's page that shows if the stock is currently open/closed for trading
closed : class="inlineblock redClockBigIcon middle isOpenExchBig-1"
opened : class="inlineblock greenClockBigIcon middle isOpenExchBig-1014"
The only attribute there is a 'class'. I want to use an 'if' statement so I could distinguished between them, I tried to run it on a 'closed' status (see the code below on 'Check', 12 lines from the bottom).
It throws an exception on the third time of the loop :
org.openqa.selenium.NoSuchElementException: no such element
Why? and please how can I fix it?
public static void main(String[] args) throws InterruptedException {
System.setProperty("webdriver.chrome.driver", "C:\\automation\\drivers\\chromedriver.exe");
WebDriver driver = new ChromeDriver();
driver.get("https://www.investing.com");
driver.navigate().refresh();
driver.findElement(By.cssSelector("[href = '/markets/']")).click();;
// list |
int size = 1;
for (int i = 0 ; i < size ; ++i) {
List <WebElement> list2 = driver.findElements(By.cssSelector("[nowrap='nowrap']>a"));
//Enter the stock page
size = list2.size();
Thread.sleep(3000);
list2.get(i).click();
**//Check**
WebElement Status = null;
if (Status == driver.findElement(By.cssSelector("[class='inlineblock redClockBigIcon middle isOpenExchBig-1']")))
{
System.out.println("Closed");
}
// Print instrument name
WebElement instrumentName = driver.findElement(By.cssSelector("[class='float_lang_base_1 relativeAttr']"));
System.out.println(instrumentName.getText());
Thread.sleep(5000);
driver.navigate().back();
}
}
}
Try using
WebElement Status = null;
if (Status == driver.findElement(By.className("redClockBigIcon")))
{
System.out.println("Closed");
}
Your loop doesn't run 3 times, but that isn't the issue here.
You're using findElement which returns a WebElement or throws an error if the element is not found. If you're on a page and you don't know if the stock is open or not, you have two options:
Catch any NoSuchElementExceptions. If this error gets thrown, the closed class is not found and therefor the page is open.
Use findElements instead of findElement. This will return a list of elements and no exception is thrown if Selenium could not find any elements. After getting the list, just check the number of elements in the list.
Option 1:
boolean isClosed = false;
try {
isClosed = driver.findElement(By.cssSelector("[class='redClockBigIcon']")).isDisplayed();
}
catch (NoSuchElementException) {
isClosed = false;
}
Option 2:
List<WebElement> closedClockElements = driver.findElements(By.cssSelector("[class='redClockBigIcon']"));
if (closedClockElements.size() > 1) {
System.out.println("Closed");
}
else {
System.out.println("Open");
}
I have a method which select the row whose rowkey contains the parameter passed into.
HTable table = new HTable(Bytes.toBytes(objectsTableName), connection);
public List<ObjectId> lookUp(String partialId) {
if (partialId.matches("[a-fA-F0-9]+")) {
// create a regular expression from partialId, which can
//match any rowkey that contains partialId as a substring,
//and then get all the row with the specified rowkey
} else {
throw new IllegalArgumentException(
"query must be done with hexadecimal values only");
}
}
I don't know how to finish code above.
I just know the following code can get the row with specified rowkey in Hbase.
String rowkey = "123";
Get get = new Get(Bytes.toBytes(rowkey));
Result result = table.get(get);
You can use RowFilter filter with RegexStringComparator to do that. Or, if it is just to fetch the rows which match a given substring you can use RowFilter with SubstringComparator. This is how you use HBase filters :
public static void main(String[] args) throws IOException {
Configuration conf = HBaseConfiguration.create();
HTable table = new HTable(conf, "demo");
Scan s = new Scan();
Filter f = new RowFilter(CompareOp.EQUAL, new SubstringComparator("abc"));
s.setFilter(f);
ResultScanner rs = table.getScanner(s);
for(Result r : rs){
System.out.println("RowKey : " + Bytes.toString(r.getRow()));
//rest of your logic
}
rs.close();
table.close();
}
The above piece of code will give you all the rows which contain abc as a part of their rowkeys.
HTH
At the start of my program, I need to read data from a MS Access database (.mdb) into a drop down control. This is done so that whenever the user types in that control, the application can auto-complete.
Anyway, the reading from database took forever so I thought I'd implement bulk row fetching.
This is the code I have:
CString sDsn;
CString sField;
sDsn.Format("ODBC;DRIVER={%s};DSN='';DBQ=%s",sDriver,sFile);
TRY
{
// Open the database
database.Open(NULL,false,false,sDsn);
// Allocate the rowset
CMultiRowset recset( &database );
// Build the SQL statement
SqlString = "SELECT NAME "
"FROM INFOTABLE";
// Set the rowset size. These many rows will be fetched in one bulk operation
recset.SetRowsetSize(25);
// Open the rowset
recset.Open(CRecordset::forwardOnly, SqlString, CRecordset::readOnly | CRecordset::useMultiRowFetch);
// Loop through each rowset
while( !recset.IsEOF() )
{
int rowsFetched = (int)recset.GetRowsFetched(); // This value is always 1 somehow
for( int rowCount = 1; rowCount <= rowsFetched; rowCount++ )
{
recset.SetRowsetCursorPosition(rowCount);
recset.GetFieldValue("NAME",sField);
m_nameDropDown.AddString(sField);
}
// Go to next rowset
recset.MoveNext();
}
// Close the database
database.Close();
}
CATCH(CDBException, e)
{
// If a database exception occured, show error msg
AfxMessageBox("Database error: "+e->m_strError);
}
END_CATCH;
MultiRowset.cpp looks like:
#include "stdafx.h"
#include "afxdb.h"
#include "MultiRowset.h"
// Constructor
CMultiRowset::CMultiRowset(CDatabase *pDB)
: CRecordset(pDB)
{
m_NameData = NULL;
m_NameDataLengths = NULL;
m_nFields = 1;
CRecordset::CRecordset(pDB);
}
void CMultiRowset::DoBulkFieldExchange(CFieldExchange *pFX)
{
pFX->SetFieldType(CFieldExchange::outputColumn);
RFX_Text_Bulk(pFX, _T("[NAME]"), &m_NameData, &m_NameDataLengths, 30);
}
MultiRowset.h looks like:
#if !defined(__MULTIROWSET_H_AD12FD1F_0566_4cb2_AE11_057227A594B8__)
#define __MULTIROWSET_H_AD12FD1F_0566_4cb2_AE11_057227A594B8__
class CMultiRowset : public CRecordset
{
public:
// Field data members
LPSTR m_NameData;
// Pointers for the lengths of the field data
long* m_NameDataLengths;
// Constructor
CMultiRowset(CDatabase *);
// Methods
void DoBulkFieldExchange(CFieldExchange *);
};
#endif
And in my database, the INFOTABLE looks like:
NAME AGE
---- ---
Name1 Age1
Name2 Age2
.
.
.
.
All I need to do is only read the data from the database. Can someone please tell me what I'm doing wrong? My code right now behaves exactly like a normal fetch. There's no bulk fetching happening.
EDIT:
I just poked around in DBRFX.cpp and found out that RFX_Text_Bulk() initializes my passed m_NameData as new char[nRowsetSize * nMaxLength]!
This means m_NameData is only a character array! I need to fetch multiple names, so wouldn't I need a 2D character array? The strangest thing is, the same RFX_Text_Bulk() initializes my passed m_NDCDataLengths as new long[nRowsetSize]. Why in the world would a character array need an array of lengths?!
According to http://msdn.microsoft.com/en-us/library/77dcbckz.aspx#_core_how_crecordset_supports_bulk_row_fetching you have to open CRecordset with CRecordset::useMultiRowFetch flag before call SetRowsetSize:
To implement bulk row fetching, you must specify the
CRecordset::useMultiRowFetch option in the dwOptions parameter of the
Open member function. To change the setting for the rowset size, call
SetRowsetSize.
You almost got it right. To fetch the values,
I would change your
for( int rowCount = 1; rowCount <= rowsFetched; rowCount++ )
{
recset.SetRowsetCursorPosition(rowCount);
recset.GetFieldValue("NAME",sField);
m_nameDropDown.AddString(sField);
}
by something like this
for( int nPosInRowset = 0; nPosInRowset < rowsFetched; nPosInRowset++ )
{
//Check if value is null
if (*(recset.m_NameDataLengths + nPosInRowset) == SQL_NULL_DATA)
continue;
CString csComboString;
csComboString = (recset.m_NameData + (nPosInRowset * 30)); //Where 30 is the size specified in RFX_Text_Bulk
m_nameDropDown.AddString(csComboString);
}
EDIT: To fetch more than one row, remove the CRecordset::forwardOnly option
EDIT 2 : You can also keep CRecordset::forwardonly, but add the CRecordset::useExtendedFetch option
Just faced the same problem.
You should use in recset.Open() call for dwOptions parameter only CRecordset::useMultiRowFetch, and not CRecordset::readOnly | CRecordset::useMultiRowFetch.
Hope this helps someone...
EDIT:- After re-check here is the situation - when using bulk recordset and opening with CRecordset::forwardOnly and CRecordset::readOnly, you must also specify CRecordset::useExtendedFetch in dwOptions. For other types of scrolling, using CRecordset::readOnly | CRecordset::useMultiRowFetch is just fine.
I have a command that I've added to a "national" node in the Sitecore Editor. The command generates a list of the 50 state nodes beneath it. It then goes through the state nodes (1 at a time) and generates a list of local nodes under each state node. Within the lists of local nodes, I iterate through them and check if the new item exists - if it does not, I add (create) it as a child under the local node. Ultimately, there are nearly 300 local items that are being added during the course of this command.
Is there a more efficient way to do this (fast query to get the 300 local nodes into one list, then check if the item exists, and create it)? If so, I'm not sure how to do that...
I'm not sure what is the most costly part of the operation. Ultimately, I'm still doing up to 300 separate queries to check if it's there, followed by insert statements, so that may still take a while to run. If so, which setting in web config will increase the applicable "timeout" setting in Sitecore? The sample structure is as follows:
//Derive the template from the name of the item (page) that was passed in - this assumes that the template name and the item name are the same
Sitecore.Data.Database database = Sitecore.Data.Database.GetDatabase("master");
TemplateItem contentPageTemplate = database.SelectSingleItem("fast:/sitecore/Templates/User Defined/Home/Pages/Local Site/" + newPage);
Sitecore.Data.Items.Item[] stateNodes = null;
Sitecore.Data.Items.Item[] localNodes = null;
Item localHomePage = null;
Item newLocalPage = null;
int webBusinessID = 0;
string ID = "";
WebBusiness business;
//Get all of the immediate child nodes (state pages) under the "parent" node ("National Locations") - and put them into a list or array
stateNodes = database.SelectItems("fast:/sitecore/content/Home/National Locations/*");
for (int i = 0; i < stateNodes.Length; i++)
{
if (stateNodes[i].Children.Count > 0)
{
localNodes = database.SelectItems("fast:/sitecore/content/Home/National Locations/" + stateNodes[i].Fields["State Abbreviation"].ToString() + "/*");
}
else
{
//Do nothing
}
for (int j = 0; j < localNodes.Length; j++)
{
localHomePage = localNodes[j];
if (localHomePage.Publishing.IsPublishable(DateTime.Now, false) == true)
{
//If the new page does not exist, create it
if (localHomePage.Children[newPage] == null)
{
newLocalPage = localHomePage.Add(newPage, contentPageTemplate);
counter = counter + 1;
}
else
{
//Additional business logic
}
}
}
}
Unless I'm missing logic/code you don't have there, I think you can simply trim this down to one query to get to the local nodes by changing the XPath:
localNodes = database.SelectItems("fast:/sitecore/content/Home/National Locations/*/*");
The change is to get all immediate sub-items of the immediate sub-items of "National Locations"