Considering I have multiple trs I want to pick one and edit value in it by clicking edit button bounded to it.
I'm doing something like this:
browser.elements('css selector', '#someId tr', elements => {
elements.value.forEach(val => {
console.log(val)
}
And I get something like this:
{
'abcd-1234-qwer-0987': 'some id'
},
{
'abcd-1234-qwer-0987': 'some other id'
}
What is exactly 'abcd-1234-qwer-0987', is this session id kind of stuff and does it change?
What is the best way to grab particular element? Because as I guess my approach is wrong: elements.value[1]['abcd-1234-qwer-0987']
When I run browser.elements and log the entries in the elements.value array, I get something like { ELEMENT: '5' }. So, to answer your questions:
What is exactly 'abcd-1234-qwer-0987', is this session id kind of stuff and does it change?
Probably some sort of session ID, but I'm not sure. Whatever it is, you don't need it for what you're doing.
What is the best way to grab particular element?
If I'm trying to select one out of several similar elements, my code ends up looking something like this:
browser.elements('css selector', '#someId', result => {
// index is defined somewhere else
browser.elementIdClick(result.value[index].ELEMENT, () => {
// whatever you want to do after you click the element.
});
});
If this doesn't work for you, can you share what OS and browser you are running on, as well as your nightwatch configuration file?
Its return the elements items are Web Element json objects. that a Web Element ID
https://nightwatchjs.org/api/commands/#elements
1.What is exactly 'abcd-1234-qwer-0987', is this session id kind of stuff and does it change?
yes it will change according to Session
2.what is the best way to grab particular element
refer the below
Using Nightwatch, how do you get the DOM element, and it's attributes from the ELEMENT entity?
browser.elements('css selector', '#someId tr', elements => {
console.log(elements);
elements.value.forEach(element =>{
let key = Object.keys(element)[0];
let ElementIDvalue = element[key];
console.log("key = ", key);
console.log("WebElementIDvalue = ", ElementIDvalue);
})
});
All of the documentation I've found around what you're trying to do says I should be able to use obj.ELEMENT but what they don't say is that it requires setting w3c to false in your nightwatch config.
Example with context:
test_settings: {
default: {
end_session_on_fail: false,
disable_error_log: false,
launch_url: 'http://localhost:3000',
desiredCapabilities : {
browserName : 'chrome',
'goog:chromeOptions' : {
// More info on Chromedriver: https://sites.google.com/a/chromium.org/chromedriver/
//
// w3c:false tells Chromedriver to run using the legacy JSONWire protocol (not required in Chrome 78)
w3c: true,
args: [
'--auto-open-devtools-for-tabs',
//'--no-sandbox',
. . .
Here is an example of how I use it:
.elements('css selector', `${targetNodeSelector} #nodeCard`, nodeContents => {
console.log(nodeContents.value)
nodeContents.value.forEach(nodeContent => {
console.log(nodeContent.ELEMENT)
// browser.elementIdText(nodeContent.ELEMENT, function (result) {
// console.log('\n' + result.value)
// })
})
})
This will display the correct keys/values:
[
{ ELEMENT: '0.944080972569816-108' },
{ ELEMENT: '0.944080972569816-109' },
{ ELEMENT: '0.944080972569816-110' },
Related
I have this code
Cypress.Commands.add('VerifyLoginState', () => {
if(cy.contains('Login')) {
cy.get('.form-control').eq(0).type('firstfield')
cy.get('.form-control').eq(1).type('secondfield')
cy.get('.btn-login').click()
cy.wait(2500)
cy.contains('Upcoming Appointments').should('be.visible')
}
else
{
cy.contains('Appointment summary').should('be.visible')
}
})
How should I write the code so that it can pass to the condition of else, when I am authenticated in the browser and the condition of if is not valid?
In other words, I want to check if an element is present on the page, and even if it is not present, the code should not give an error and move on
Cypress yields the result of cy functions, and does not return them. So your if/else will not work as it would in traditional JavaScript. Check out this article from Cypress about conditional testing.
Something like the following should help you out:
// Get the body of the DOM
cy.get('body').then(($body) => {
// Check if the body contains the `Login` element
if ($body.contains('Login').length) {
cy.get('.form-control').eq(0).type('firstfield')
cy.get('.form-control').eq(1).type('secondfield')
cy.get('.btn-login').click()
cy.wait(2500)
cy.contains('Upcoming Appointments').should('be.visible')
} else {
cy.contains('Appointment summary').should('be.visible')
}
Another option is to use within
ref: https://docs.cypress.io/api/commands/within#Syntax
cy.contains('Login')
.within(() => {
cy.get('.form-control').eq(0).type('firstfield')
cy.get('.form-control').eq(1).type('secondfield')
cy.get('.btn-login').click()
cy.wait(2500)
cy.contains('Upcoming Appointments').should('be.visible')
})
You may find it easier to test for either/or text using jQuery :contains and multiple selectors separated by a comma
cy.get('body')
.children(':contains(Login), :contains(Appointment summary)') // only one is on the page
.invoke('text')
.then(labelText => {
if (labelText.trim() === 'Login') {
cy.get('.form-control').eq(0).type('firstfield')
... etc
} else {
cy.contains('Appointment summary').should('be.visible')
}
})
How to check if element is present or not, so that certain steps can be performed if element is present. Else certain different steps can be performed if element is not present.
I tried something like below but it didn't work:
Cypress.Commands.add('deleteSometheingFunction', () => {
cy.get('body').then($body => {
if ($body.find(selectors.ruleCard).length) {
let count = 0;
cy.get(selectors.ruleCard)
.each(() => count++)
.then(() => {
while (count-- > 0) {
cy.get('body')
// ...
// ...
}
});
}
});
});
I am looking for a simple solution, which can be incorporated with simple javascript
if else block or then() section of the promise
Something similar to Webdriver protocol's below implementions:
driver.findElements(By.yourLocator).size() > 0
check for presenece of element in wait
Kindly advise. Thanks
I'll just add that if you decide to do if condition by checking the .length property of cy.find command, you need to respect the asynchronous nature of cypress.
Example:
Following condition evaluates as false despite appDrawerOpener button exists
if (cy.find("button[data-cy=appDrawerOpener]").length > 0) //evaluates as false
But this one evaluates as true because $body variable is already resolved as you're in .then() part of the promise:
cy.get("body").then($body => {
if ($body.find("button[data-cy=appDrawerOpener]").length > 0) {
//evaluates as true
}
});
Read more in Cypress documentation on conditional testing
it has been questioned before: Conditional statement in cypress
Thus you can basically try this:
cy.get('header').then(($a) => {
if ($a.text().includes('Account')) {
cy.contains('Account')
.click({force:true})
} else if ($a.text().includes('Sign')) {
cy.contains('Sign In')
.click({force:true})
} else {
cy.get('.navUser-item--account .navUser-action').click({force:true})
}
})
cypress all steps are async ,, so that you should make common function in commands file or page object file,,..
export function checkIfEleExists(ele){
return new Promise((resolve,reject)=>{
/// here if ele exists or not
cy.get('body').find( ele ).its('length').then(res=>{
if(res > 0){
//// do task that you want to perform
cy.get(ele).select('100').wait(2000);
resolve();
}else{
reject();
}
});
})
}
// here check if select[aria-label="rows per page"] exists
cy.checkIfEleExists('select[aria-label="rows per page"]')
.then(e=>{
//// now do what if that element is in ,,..
})
.catch(e=>{
////// if not exists...
})
I found a solution, hope it helps!
You can use this:
cy.window().then((win) => {
const identifiedElement = win.document.querySelector(element)
cy.log('Object value = ' + identifiedElement)
});
You can add this to your commands.js file in Cypress
Cypress.Commands.add('isElementExist', (element) => {
cy.window().then((win) => {
const identifiedElement = win.document.querySelector(element)
cy.log('Object value = ' + identifiedElement)
});
})
Cypress official document has offered a solution addressing the exact issue.
How to check Element existence
// click the button causing the new
// elements to appear
cy.get('button').click()
cy.get('body')
.then(($body) => {
// synchronously query from body
// to find which element was created
if ($body.find('input').length) {
// input was found, do something else here
return 'input'
}
// else assume it was textarea
return 'textarea'
})
.then((selector) => {
// selector is a string that represents
// the selector we could use to find it
cy.get(selector).type(`found the element by selector ${selector}`)
})
For me the following command is working for testing a VS code extension inside Code server:
Cypress.Commands.add('elementExists', (selector) => {
return cy.window().then($window => $window.document.querySelector(selector));
});
And I'm using it like this in my E2E test for a Code Server extension:
cy.visit("http://localhost:8080");
cy.wait(10000); // just an example here, better use iframe loaded & Promise.all
cy.elementExists("a[title='Yes, I trust the authors']").then((confirmBtn) => {
if(confirmBtn) {
cy.wrap(confirmBtn).click();
}
});
Just ensure that you're calling this check once everything is loaded.
If you're using Tyepscript, add the following to your global type definitions:
declare global {
namespace Cypress {
interface Chainable<Subject> {
/**
* Check if element exits
*
* #example cy.elementExists("#your-id").then($el => 'do something with the element');
*/
elementExists(element: string): Chainable<Subject>
}
}
}
Aside
VS Code server relies heavily on Iframes which can be hard to test. The following blog post will give you an idea - Testing iframes with Cypress.
The above code is needed to dismiss the "trust modal" if it's shown. Once the feature disable-workspace-trust is released it could be disabled as CLI option.
This command throws no error if element does not exist. If it does, it returns the actual element.
cypress/support/commands.js
elementExists(selector) {
cy.get('body').then(($body) => {
if ($body.find(selector).length) {
return cy.get(selector)
} else {
// Throws no error when element not found
assert.isOk('OK', 'Element does not exist.')
}
})
},
Usage:
cy.elementExists('#someSelectorId').then(($element) => {
// your code if element exists
})
In case somebody is looking for a way to use cy.contains to find an element and interact with it based on the result. See this post for more details about conditional testing.
Use case for me was that user is prompted with options, but when there are too many options, an extra click on a 'show more' button needs to be done before the 'desired option' could be clicked.
Command:
Cypress.Commands.add('clickElementWhenFound', (
content: string,
) => {
cy.contains(content)
// prevent previous line assertion from triggering
.should((_) => {})
.then(($element) => {
if (!($element || []).length) {
/** Do something when element was not found */
} else {
cy.contains(content).click();
}
});
});
Usage:
// Click button with 'Submit' text if it exists
cy.clickElementWhenFound('Submit');
Using async/await gives a clean syntax:
const $el = await cy.find("selector")
if ($el.length > 0) {
...
More info here: https://medium.com/#NicholasBoll/cypress-io-using-async-and-await-4034e9bab207
I had the same issue like button can appear in the webpage or not. I fixed it using the below code.
export function clickIfExist(element) {
cy.get('body').then((body) => {
cy.wait(5000).then(() => {
if (body.find(element).length > 0) {
cy.log('Element found, proceeding with test')
cy.get(element).click()
} else {
cy.log('Element not found, skipping test')
}
})
})
}
Im trying to deal with testing tools likewise nightmare, phantom etc. And seems to be stuck with some basic DOM manipulation. Im using jquery here for later use of $().parent() methods. Ive already tryed all posiible selector that come in collection with no use. It only returns some pieces of data. Where it actually fully exists on a page.
....
nightmare
.goto(link)
.inject('js', 'jquery-2.2.4.min.js')
.wait()
.evaluate( () => {
$('.sport--head:not(.folding--open)').click()
})
.wait(4000)
.evaluate( () => {
let events = [];
$('.view-wrapper .events--list > li').each( (i, elem) => {
let event = {
'name' : $(elem).text()
}
events.push(event);
});
let data = JSON.stringify(events, null, '\t');
return data;
})
.end()
....
It returns empty fields where they are actually not:
[{ "name": "" }, { "name": "" }, { "name": "" }, { "name": "contents" } ]
Why could this happen? Any ideas?
You do not know the pages loads completely within 4 seconds.
Either there are some Ajax data and while you are scraping it appears only at that moment. Or, the selector is actually wrong.
Instead of a specific number, use .wait to wait for a target selector,
.wait(4000)
.wait('.view-wrapper .events--list > li')
Also make sure the code actually returns the data by putting it on console.log of your actual browser.
I noticed that with VBO you can call the method getSingleNode to get a specific node object, is it possible to do something similar with office js?
Also, I have a radio button value in my Word metadata, I managed to access its customxmlnode object, then I used setTextsync method to change its value from true to false, but the new value I get for my radio button metadata is empty. Other text type metadata could be edited correctly though.
Could anyone give some suggestions?
function EditCTF() {
//$("#fields").css({ display: "" });
Word.run(function(context) {
context.document.properties.title = $("#Title").val();
Office.context.document.customXmlParts.getByNamespaceAsync(
"http://schemas.microsoft.com/office/2006/metadata/properties",
function(asyncResult) {
if (asyncResult.value.length > 0) {
var xmlPart = asyncResult.value[0];
xmlPart.getNodesAsync("*/*", function(nodeResults) {
console.log(nodeResults.value.length);
for (i = 0; i < nodeResults.value.length; i++) {
var node = nodeResults.value[i];
node.getTextAsync({ asyncContext: "StateNormal" }, function(result) {
console.log(result);
console.log(result.value);
});
console.log("NewValue");
if (node.baseName == "Address") {
node.setTextAsync(
$("#Address").val(),
{
asyncContext: "StateNormal"
},
function(newresult) {}
);
}
if (node.baseName == "MainContactPerson") {
node.setTextAsync(
$("#Main Contact Person").val(),
{
asyncContext: "StateNormal"
},
function(newresult) {}
);
}
if (node.baseName == "GDPR") {
node.setTextAsync(
"true",
{
asyncContext: "StateNormal"
},
function(newresult) {
console.log(newresult);
console.log(newresult.value);
}
);
}
}
});
}
}
);
return context.sync().then(function() {});
});
}
The correct XPATH format is not an intuitive format, and it does not help that the Microsoft API documentation provide dumb examples that use wildcard (/) xpaths, like so:
xmlPart.getNodesAsync('*', function (nodeResults)
OfficeJS (or perhaps, SharePoint) injects namespace aliases into the mix (ie /ns3:...), so you were on the right track, but to get the 'CGI_Address' SharePoint document property, you need this XPATH syntax:
var xpath = "/ns0:properties[1]/documentManagement[1]/ns3:CGI_Address[1]";
xmlPart.getNodesAsync(xpath, ...);
GOTCHA: When making significant schema changes to the SharePoint content types that the Word document is based on, it may suddenly change the namespace alias from "ns3" to "ns4" or indeed, from "ns4" back to "ns3" like what happened to me today - go figure?!?
And it seems that the OfficeJS API does not properly implement XPATH, as trying to wildcard the namespace alias (so it can accept /ns3 or /ns4 etc) with /*:CGI_Address1 doesn't work.
SO Reference (as to why it should work) - how to ignore namespaces with XPath
Please use the XPATH expression you send in the xmlPart.getNodesAsync("/", function(nodeResults) method. The first parameter is an XPath expression you can use to get the single node you need.
I have seen other questions with the same issue , the data seems the be the issue most of the time.
I did create a jsfiddle http://jsfiddle.net/gwvoqns8/
(always keep in mind that http has to be used, and not https )
that should demonstrate the problem
I want to have whatever is typed into the text box to display as another named node of whatever name...
The code I'm using seems to enforce me to selected an existing parent node and i do not want that.
Its a bit annoying why it is saying "undefined"
$("#addTopLevel").click(function () {
console.log('in this');
if (treeview.select().length) {
console.log('1');
treeview.append({
text: $("#appendNodeText").val()
}, treeview.select());
} else {
//alert("please select tree node");
console.log('2');
}
});
Try this:
var ds = new kendo.data.HierarchicalDataSource({
data: [
{"Id":1,"ReportGroupName":"Standard Reports","ReportGroupNameResID":null,"SortOrder":1},
{"Id":2,"ReportGroupName":"Custom Reports","ReportGroupNameResID":null,"SortOrder":2},
{"Id":3,"ReportGroupName":"Retail Reports","ReportGroupNameResID":null,"SortOrder":3},
{"Id":4,"ReportGroupName":"Admin Reports","ReportGroupNameResID":null,"SortOrder":5},
{"Id":5,"ReportGroupName":"QA Reports","ReportGroupNameResID":null,"SortOrder":4}
]
});
var treeview = $("#treeview").kendoTreeView({
dataSource: ds,
dataTextField: "ReportGroupName",
loadOnDemand: false
}).data("kendoTreeView");
treeview.expand(".k-item");
$("#addTopLevel").click(function(e) {
var selectedNode = treeview.select();
// passing a falsy value as the second append() parameter
// will append the new node to the root group
if (selectedNode.length == 0) {
selectedNode = null;
}
treeview.append({
ReportGroupName: $("#appendNodeText").val()
}, selectedNode);
});
I was noticing that you could just do this
treeview.append({
ReportGroupName: $("#appendNodeText").val()
}, null);
However, OP answer would be better as then you will have the ability to add to children nodes if you wanted to, even though so said you didn't want to.
cheers