Selecting Ajax Dropdown suggestion list using Selenium for Firefox - ajax

How can i select Ajax Dropdown suggestion list item using selenium code for firefox??
My problem is :the Ajax dropdown list is visible but it is not selected and next steps gets stuck.
May be selenium is waiting for something.
the list that page populates is dynamic and in bla bla tags.
Please help with a example code.
How can i use waitfor* here.
Remember i am not using firefox ide but i am writing a code.
Please help.

I had a similar problem whereby, selenium was able to find the dropdown menu but was unable to click on the visible text. I later found out that there was an Ajax call that was populating the dropdown menu data and as a result selenium seemed to not be able to select the intended visible text because the list items had not been fully populated. That is, by the time the script was selecting my option value, Ajax had not completely loaded the menu options. Here's my solution:
public void nameOfCollegeList(String optionItem) {
// declare the dropdownMenu web element
WebElement dropDownMenu = driver.findElement(By.cssSelector("#CollegeNames"));
// click on the dropdownMenu element to initiate Ajax call
dropDownMenu.click();
// keep checking the drop down menu item list until you find the desired text that indicates that the menu has
// been fully loaded. In this example I always expect "Other (please specify)" to be the last item in the drop down menu.
// If I don't find the expected last item in the list in my if condition, execute the else condition by calling the
// same method(recursively). Please note that if the "if" statement is never satisfied then you'll end up with an
// infinite loop.
if (dropDownMenu.getText().contains("Other (please specify)")) {
new Select(dropDownMenu).selectByVisibleText(optionItem);
}
else {
nameOfCollegeList(optionItem);
}
}

i am little confused with your question at " :the Ajax dropdown list is visible but it is not selected "
this sounds like that the element is disabled. (Java coding)
if so selenium.isElementDisabled()
if not then,
1) programming laguage solution using while loop and isElementPresent() OR isElementDisabled()
//trigger the Ajax request and then
long initialTime = System.currentTimeMillis();
do{
thread.sleep(1000);
}while((!selenium.isElementPresent("AjaxElement")) && (System.getCurrentTimeMillis() - initialTime <= 5000)) ;
//some thing like above for client programming solution...but for,
2) selenium's inbuilt solution
we have a method called waitForCondition("java script to be executed", "time out value");
this method loops the javascript statement until it returns true or the supplied time out occurs
here the important thing is analyzing the application/Ajax element to find out which particular condition of the element changes.
from your explation my guess is this, display=none will be changed to display=block OR
disabled=true will be changed to disabled=false OR isReadOnly will be changed to no such attribute ect.....(you need to figure out this)
and then, use this attribute = value to build a javascript function as ,
selenium.waitForCondition("window.document.getElementById('AJAX ELEMENT').disabled == 'false'", "3000");
you can work out the above statement however you want in your programming language.
try {
//do the action which triggers the Ajax call
selenium.waitForCondition("window.document.getElementById('AJAX ELEMENT[drop down element]').disabled == 'false'", "3000");
//OR
selenium.waitForCondition("window.document.getElementById('AJAX ELEMENT').disabled == 'false'", "3000");
}
catch(SeleniumException se)
{
if((se.getMessage()).toLowerCase().contains("timed out")
throw //..some a custom exception however your organisation requires
}
selenium.select("drop down element id", "option id");
and so on.....

Related

Conditional Tests in Cypress using data-cy attributes as selectors

I see some posts about this exact topic, but none of them using data classes like I am as selectors, so it makes this conditional test a bit harder to write.
The idea is that I have a table with pagination on it. My idea is to check if the [data-cy-pagination-next] has or doesn't have the disabled attribute on it, which would mean there's more than one page and therefore the test can continue.
Most posts I see use a syntax like this:
cy.get('my-button')
.then($button => {
if ($button.is(':enabled')) {
cy.wrap($button).click()
}
})
But I don't have the $button like they described. What I would be clicking on is a button, but does that really matter?
It doesn't seem like I can write
cy.get('[data-cy=pagination-next]')
.then('[data-cy=pagination-next]' => {
if ('[data-cy=pagination-next]'.is(':enabled')) {
cy.wrap('[data-cy=pagination-next]').click()
}
})
How can I get this conditional to work?
If there is more than one page, this test works great, but in the cases that there is no second page, I just want the test to end there.
Any tips would be greatly appreciated!
Cheers!
Here is the test currently
it('Data Source has Pagination and test functionality', () => {
cy.get('[data-cy=pagination]').should('exist')
// assert that we are at the first page and the start and back button is disabled
cy.get('[data-cy=pagination-page-list]').contains('Page 1 of')
// If there are multiple pages then do the following tests
// click next button and assert that the current page is page 2
cy.get('[data-cy=pagination-next]').click()
cy.get('[data-cy=pagination-page-list]').contains('Page 2 of')
// click end button and assert that the end and next buttons are disabled
cy.get('[data-cy=pagination-end]').click()
cy.get('[data-cy=pagination-next]').should('be.disabled')
cy.get('[data-cy=pagination-end]').should('be.disabled')
// click start button button and assert that the current page is page 1 and next and start buttons are disabled
cy.get('[data-cy=pagination-start]').click()
cy.get('[data-cy=pagination-page-list]').contains('Page 1 of')
cy.get('[data-cy=pagination-start]').should('be.disabled')
cy.get('[data-cy=pagination-back]').should('be.disabled')
})
You can use the page indicator to split the test logic
it('Data Source has Pagination and test functionality', () => {
cy.get('[data-cy=pagination]').should('exist')
cy.get('[data-cy=pagination-page-list]')
.then($pageList => {
if ($pageList.text() === 'Page 1 of 1')
// single page assertions
cy.get('[data-cy=pagination-next]').should('be.disabled')
cy.get('[data-cy=pagination-end]').should('be.disabled')
cy.get('[data-cy=pagination-start]').should('be.disabled')
cy.get('[data-cy=pagination-back]').should('be.disabled')
} else {
// multi page assertions
cy.get('[data-cy=pagination-next]').click()
cy.get('[data-cy=pagination-page-list]')
.should('contain', 'Page 2 of') // assert on second page
cy.get('[data-cy=pagination-end]').click()
cy.get('[data-cy=pagination-next]').should('be.disabled')
cy.get('[data-cy=pagination-start]').click()
cy.get('[data-cy=pagination-page-list]').contains('Page 1 of')
cy.get('[data-cy=pagination-start]').should('be.disabled')
cy.get('[data-cy=pagination-back]').should('be.disabled')
}
})
Better still, control the test data so that only a single page exists, then run two tests under known conditions and eliminate flaky conditional testing.
I think you're misunderstanding how yielding and callbacks work. The reason there is $button in the .then() is because it is yielded by cy.get(). It could be named anything, so long as it is a valid name (note: a string literal, like you are trying to do, is not valid).
So, $button is just the yielded element from your cy.get('my-button'). Which is why we can then use JQuery functions and Chai assertions on it.
cy.get('[data-cy=pagination-next]')
.then($el => { // naming the yielded object from `cy.get()` to $el
if ($el.is(':enabled')) { // using JQuery function `.is` to check if the element is enabled
cy.wrap($el).click() // Cypress requires the JQuery element to be wrapped before it can click it.
}
})
You can do something like this. You can use an each to loop over all the pagination elements. So in case, you don't have only 2 buttons the loop will check for only 2 buttons and then terminate.
cy.get('[data-cy=pagination-page-list]').should('contain.text', 'Page 1 of')
cy.get('[data-cy=pagination-next]').each(($ele, index) => {
if ($ele.is(':enabled')) {
cy.wrap($ele).click()
cy.wrap($ele).should('contain.text', `Page ${index + 2} of`) //index starts from 0
}
})

Cypress : The submit button is disabled even after all the text fields have filled

I was trying to automate a text submit form using cypress. The 'Create student' button is disabled even after all the fields have been filled
Please see the cypress error
code :
it('should be able to add a new student and update the details, remove from the class and delete the account', function () {
cy.visit(
'https://readingeggs.blake-staging.com/district_ui#/reading/manage-schools/students/195286/new'
)
cy.findByLabelText('First Name').type('ark')
cy.get('#first-name').should('have.value', 'ark')
cy.findByLabelText('Last Name').type('last')
cy.get('#last-name').should('have.value', 'last')
cy.get('[data-test-select-grade]').select('1')
cy.get('#grade-dropdown').should('have.value', '1')
cy.get('[data-test-select-teacher]').select('Lehner, Abbey')
cy.get('#teacher-dropdown').should('have.value', '3068134')
cy.get('[data-test-submit-new-student]').click()
cy.get('#main')
.findByRole('alert')
.should('include.text', `Successfully created a student`)
})
})
Be careful using click({force:true}) as suggested in the error message, there may be another problem that your test will now ignore!
You can first try an assertion that the button is not disabled.
Sometimes the test can run too quickly, and the web page has not yet enabled the button before the test tries to click it.
Adding .should('not.be.disabled') will retry this check for up to 4 seconds, which should be enough time for the page to complete changes.
cy.get('[data-test-submit-new-student]')
.should('not.be.disabled')
.click()
If using .should('not.be.disabled') does not work (I agree, it should be the first thing to try), try adding a trigger event to each input - in case the .type() command is not triggering the validation change.
cy.findByLabelText('First Name').type('ark').trigger('change')
cy.get('#first-name').should('have.value', 'ark')
cy.findByLabelText('Last Name').type('last').trigger('change')
cy.get('#last-name').should('have.value', 'last')
cy.get('[data-test-select-grade]').select('1').trigger('change')
cy.get('#grade-dropdown').should('have.value', '1')
cy.get('[data-test-select-teacher]').select('Lehner, Abbey').trigger('change')
cy.get('#teacher-dropdown').should('have.value', '3068134')
cy.get('[data-test-submit-new-student]').click()
If still no joy, use .click({force:true})
By the way, cy.get('[data-test-select-grade]').select('1') looks a bit suspicious. The select command can take a display value as a string or a position value as a number. The screenshot shows "K" is selected, so I would expect either of these to work
cy.get('[data-test-select-grade]').select(1) // number passed
// or
cy.get('[data-test-select-grade]').select('K') // string passed
One option would be to use {force: true} with click().
it('should be able to add a new student and update the details, remove from the class and delete the account', function () {
cy.visit(
'https://readingeggs.blake-staging.com/district_ui#/reading/manage-schools/students/195286/new'
)
cy.findByLabelText('First Name').type('ark')
cy.get('#first-name').should('have.value', 'ark')
cy.findByLabelText('Last Name').type('last')
cy.get('#last-name').should('have.value', 'last')
cy.get('[data-test-select-grade]').select('1')
cy.get('#grade-dropdown').should('have.value', '1')
cy.get('[data-test-select-teacher]').select('Lehner, Abbey')
cy.get('#teacher-dropdown').should('have.value', '3068134')
cy.get('[data-test-submit-new-student]').click({force: true})
cy.get('#main')
.findByRole('alert')
.should('include.text', 'Successfully created a student')
})

Selecting an element not equal to a certain string

I am trying to select an incorrect answer (radio button) to get an error message to appear, but the answers are random (except the correct answer).
How can I say get the radio buttons, and then click one that does not equal "correct answer" using cypress assertions?
cy.get('[data-cy="multipleChoiceQuestionAnswer"]')
.should('not.contain', 'correct answer')//.find('label').not('corect answer')//.not.includes('correct answer')
.click()
I would like to be able to select one of the two radio buttons for the incorrect answers, right now I can only select the correct answer.
well:
be aware that .should('not.contain', 'correct answer') is an assertion, is not a way to filter/get some elements.
It's, essentially, just a way to check (aka "assert") that something is like you expect it to be.
An assertion like yours is useful just to get the Cypress log print something like this
Read it like if you are telling
"Ehy Cypress, I selected an element, could you check that it doesn't contain the correct answer, please?"
What are assertions useful for? They aren't useful when everything goes right but when the test goes wrong.
Because without assertions, you can find yourself behind a broken test with Cypress telling you that "there isn't the element" but you can't know which element Cypress isn't finding.
Placing some "key point" assertions allows you to understand why a test failed in short time.
Anyway: if your HTML is something like this
<div data-cy="multipleChoiceQuestionAnswer"><label>correct answer<input type="checkbox"/></label></div>
<div data-cy="multipleChoiceQuestionAnswer"><label>no<input type="checkbox"/></label></div>
<div data-cy="multipleChoiceQuestionAnswer"><label>nope<input type="checkbox"/></label></div>
you can accomplish your goal making:
cy.get('[data-cy="multipleChoiceQuestionAnswer"]').then(els => {
// `els` is a jQuery instance, let's parse the various elements
let $el;
for(let i = 0, n = els.length; i < n; i++) {
// it transforms every element in a jQuery instance
$el = Cypress.$(els[i]);
// it uses jQuery to get the label text
if($el.find("label").text() !== "correct answer") {
// it stops as soon as the answer isn't the correct one
break;
}
}
// returns the element to be clicked
return $el.find("input");
})
// it assert about it (to have a useful hint in the Cypress command log)
.should("not.contain", "correct answer")
// clicks it
.click();
I hope the code is self-explanatory (in case it isn't, ask me some more clarifications) 😊

Checking Text exist or not?

Say I am on StackOverFlow "All Questions" page.
I want to search for a user say "user2402616" (which is present on 3rd page) on first page.
If not present then click on next button until it is found.
I tried below code using Xpath but getting error.
boolean bPres = driver.findElement(By.xpath("//a[contains(text(),'user2402616')]")).isDisplayed();
System.out.println(bPres);
while (!bPres) {
driver.findElement(By.xpath("//a[contains(text(),'next')]")).click();
}
Since the xpath has text in both the cases i.e for clicking on the user and the next button . You can use the By.linkText method and pass in the value which you want to click
boolean bPres = driver.findElement(By.linkText("user2402616")).isDisplayed();
System.out.println(bPres);
while (!bPres) {
driver.findElement(By.xpath("next").click();
}

How to debug Google Apps Script (aka where does Logger.log log to?)

In Google Sheets, you can add some scripting functionality. I'm adding something for the onEdit event, but I can't tell if it's working. As far as I can tell, you can't debug a live event from Google Sheets, so you have to do it from the debugger, which is pointless since the event argument passed to my onEdit() function will always be undefined if I run it from the Script Editor.
So, I was trying to use the Logger.log method to log some data whenever the onEdit function gets called, but this too seems like it only works when run from the Script Editor. When I run it from the Script Editor, I can view the logs by going to View->Logs...
I was hoping I'd be able to see the logs from when the event actually gets executed, but I can't figure it out.
How do I debug this stuff?
UPDATE:
As written in this answer,
Stackdriver Logging is the preferred method of logging now.
Use console.log() to log to Stackdriver.
Logger.log will either send you an email (eventually) of errors that have happened in your scripts, or, if you are running things from the Script Editor, you can view the log from the last run function by going to View->Logs (still in script editor). Again, that will only show you anything that was logged from the last function you ran from inside Script Editor.
The script I was trying to get working had to do with spreadsheets - I made a spreadsheet todo-checklist type thing that sorted items by priorities and such.
The only triggers I installed for that script were the onOpen and onEdit triggers. Debugging the onEdit trigger was the hardest one to figure out, because I kept thinking that if I set a breakpoint in my onEdit function, opened the spreadsheet, edited a cell, that my breakpoint would be triggered. This is not the case.
To simulate having edited a cell, I did end up having to do something in the actual spreadsheet though. All I did was make sure the cell that I wanted it to treat as "edited" was selected, then in Script Editor, I would go to Run->onEdit. Then my breakpoint would be hit.
However, I did have to stop using the event argument that gets passed into the onEdit function - you can't simulate that by doing Run->onEdit. Any info I needed from the spreadsheet, like which cell was selected, etc, I had to figure out manually.
Anyways, long answer, but I figured it out eventually.
EDIT:
If you want to see the todo checklist I made, you can check it out here
(yes, I know anybody can edit it - that's the point of sharing it!)
I was hoping it'd let you see the script as well. Since you can't see it there, here it is:
function onOpen() {
setCheckboxes();
};
function setCheckboxes() {
var checklist = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("checklist");
var checklist_data_range = checklist.getDataRange();
var checklist_num_rows = checklist_data_range.getNumRows();
Logger.log("checklist num rows: " + checklist_num_rows);
var coredata = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("core_data");
var coredata_data_range = coredata.getDataRange();
for(var i = 0 ; i < checklist_num_rows-1; i++) {
var split = checklist_data_range.getCell(i+2, 3).getValue().split(" || ");
var item_id = split[split.length - 1];
if(item_id != "") {
item_id = parseInt(item_id);
Logger.log("setting value at ("+(i+2)+",2) to " + coredata_data_range.getCell(item_id+1, 3).getValue());
checklist_data_range.getCell(i+2,2).setValue(coredata_data_range.getCell(item_id+1, 3).getValue());
}
}
}
function onEdit() {
Logger.log("TESTING TESTING ON EDIT");
var active_sheet = SpreadsheetApp.getActiveSheet();
if(active_sheet.getName() == "checklist") {
var active_range = SpreadsheetApp.getActiveSheet().getActiveRange();
Logger.log("active_range: " + active_range);
Logger.log("active range col: " + active_range.getColumn() + "active range row: " + active_range.getRow());
Logger.log("active_range.value: " + active_range.getCell(1, 1).getValue());
Logger.log("active_range. colidx: " + active_range.getColumnIndex());
if(active_range.getCell(1,1).getValue() == "?" || active_range.getCell(1,1).getValue() == "?") {
Logger.log("made it!");
var next_cell = active_sheet.getRange(active_range.getRow(), active_range.getColumn()+1, 1, 1).getCell(1,1);
var val = next_cell.getValue();
Logger.log("val: " + val);
var splits = val.split(" || ");
var item_id = splits[splits.length-1];
Logger.log("item_id: " + item_id);
var core_data = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("core_data");
var sheet_data_range = core_data.getDataRange();
var num_rows = sheet_data_range.getNumRows();
var sheet_values = sheet_data_range.getValues();
Logger.log("num_rows: " + num_rows);
for(var i = 0; i < num_rows; i++) {
Logger.log("sheet_values[" + (i) + "][" + (8) + "] = " + sheet_values[i][8]);
if(sheet_values[i][8] == item_id) {
Logger.log("found it! tyring to set it...");
sheet_data_range.getCell(i+1, 2+1).setValue(active_range.getCell(1,1).getValue());
}
}
}
}
setCheckboxes();
};
As far as I can tell, you can't debug a live event from google docs, so you have to do it from the debugger, which is pointless since the event argument passed to my onEdit() function will always be undefined if I run it from the Script Editor.
True - so define the event argument yourself for debugging. See How can I test a trigger function in GAS?
I was trying to use the Logger.log method to log some data whenever the onEdit function gets called, but this too seems like it only works when run from the Script Editor. When I run it from the Script Editor, I can view the logs by going to View->Logs...
True again, but there is help. Peter Hermann's BetterLog library will redirect all logs to a spreadsheet, enabling logging even from code that is not attached to an instance of the editor / debugger.
If you're coding in a spreadsheet-contained script, for example, you can add just this one line to the top of your script file, and all logs will go to a "Logs" sheet in the spreadsheet. No other code necessary, just use Logger.log() as you usually would:
Logger = BetterLog.useSpreadsheet();
2017 Update:
Stackdriver Logging is now available for Google Apps Script. From the menu bar in the script editor, goto:
View > Stackdriver Logging to view or stream the logs.
console.log() will write DEBUG level messages
Example onEdit() logging:
function onEdit (e) {
var debug_e = {
authMode: e.authMode,
range: e.range.getA1Notation(),
source: e.source.getId(),
user: e.user,
value: e.value,
oldValue: e. oldValue
}
console.log({message: 'onEdit() Event Object', eventObject: debug_e});
}
Then check the logs in the Stackdriver UI labeled onEdit() Event Object to see the output
I've gone through these posts and somehow ended up finding a simple answer, which I'm posting here for those how want short and sweet solutions:
Use console.log("Hello World") in your script.
Go to https://script.google.com/home/my and select your add-on.
Click on the ellipsis menu on Project Details, select Executions.
Click on the header of the latest execution and read the log.
A little hacky, but I created an array called "console", and anytime I wanted to output to console I pushed to the array. Then whenever I wanted to see the actual output, I just returned console instead of whatever I was returning before.
//return 'console' //uncomment to output console
return "actual output";
}
If you have the script editor open you will see the logs under View->Logs. If your script has an onedit trigger, make a change to the spreadsheet which should trigger the function with the script editor opened in a second tab. Then go to the script editor tab and open the log. You will see whatever your function passes to the logger.
Basically as long as the script editor is open, the event will write to the log and show it for you. It will not show if someone else is in the file elsewhere.
I am having the same problem, I found the below on the web somewhere....
Event handlers in Docs are a little tricky though. Because docs can handle multiple simultaneous edits by multiple users, the event handlers are handled server-side. The major issue with this structure is that when an event trigger script fails, it fails on the server. If you want to see the debug info you'll need to setup an explicit trigger under the triggers menu that emails you the debug info when the event fails or else it will fail silently.
It's far from elegant, but while debugging, I often log to the Logger, and then use getLog() to fetch its contents. Then, I either:
save the results to a variable (which can be inspected in the Google Scripts debugger—this works around cases where I can't set a breakpoint in some code, but I can set one in code that gets executed later)
write it to some temporary DOM element
display it in an alert
Essentially, it just becomes a JavaScript output issue.
It grossly lacks the functionality of modern console.log() implementations, but the Logger does still help debug Google Scripts.
Just as a notice. I made a test function for my spreadsheet. I use the variable google throws in the onEdit(e) function (I called it e). Then I made a test function like this:
function test(){
var testRange = SpreadsheetApp.getActiveSpreadsheet().getSheetByName(GetItemInfoSheetName).getRange(2,7)
var testObject = {
range:testRange,
value:"someValue"
}
onEdit(testObject)
SpreadsheetApp.getActiveSpreadsheet().getSheetByName(GetItemInfoSheetName).getRange(2,6).setValue(Logger.getLog())
}
Calling this test function makes all the code run as you had an event in the spreadsheet. I just put in the possision of the cell i edited whitch gave me an unexpected result, setting value as the value i put into the cell.
OBS! for more variables googles gives to the function go here: https://developers.google.com/apps-script/guides/triggers/events#google_sheets_events
Currently you are confined to the container bound nature of using scripts within docs. If you create a new script inside outside of docs then you will be able to export information to a google spreadsheet and use it like a logging tool.
For example in your first code block
function setCheckboxes() {
// Add your spreadsheet data
var errorSheet = SpreadsheetApp.openById('EnterSpreadSheetIDHere').getSheetByName('EnterSheetNameHere');
var cell = errorSheet.getRange('A1').offset(errorSheet.getLastRow(),0);
// existing code
var checklist = SpreadsheetApp.getActiveSpreadsheet().getSheetByName("checklist");
var checklist_data_range = checklist.getDataRange();
var checklist_num_rows = checklist_data_range.getNumRows();
// existing logger
Logger.log("checklist num rows: " + checklist_num_rows);
//We can pass the information to the sheet using cell.setValue()
cell.setValue(new Date() + "Checklist num rows: " + checklist_num_rows);
When I'm working with GAS I have two monitors ( you can use two windows ) set up with one containing the GAS environment and the other containing the SS so I can write information to and log.
The dev console will log errors thrown by the app script, so you can just throw an error to get it logged as a normal console.log. It will stop execution, but it might still be useful for step by step debugging.
throw Error('hello world!');
will show up in the console similarly to console.log('hello world')
For Apps Script projects that are tied to a single Sheet (or doc) — in 2022 — there is no View menu like other answers suggest. Instead you need to look in the Executions menu on the left sidebar to see the executions of your onSelectionChange function (or any other function), from there you can click REFRESH until your console.log messages appear.
just debug your spreadsheet code like this:
...
throw whatAmI;
...
shows like this:

Resources