grabAttributeFrom() method not returning the attribute value - testing

I am trying to get the attribute value of an element by using I.grabAttributeFrom() method but I always get undefined instead of the attribute value. My code is
Scenario('Smoketest', async (I) => {
const columnIndex = await I.grabAttributeFrom('//th[text()="Status"]', 'aria-colindex');
});
The element is like that
<th aria-colindex = "2">
"Status"
<span> ... </span>
</th>
And I am using testcafe in codeceptjs.

I wasn't able to get it to work either, so I wrote a protractor helper that worked for me to grab text attributes:
/**
* Function to return the text content of all elements matching with the locator in an array
* #param xpath object
*/
async getElementsText(locator) {
const driver = this.helpers.Protractor.browser;
await driver.waitForAngular();
// await console.log("Getting text for: " + locator.xpath);
return driver.element.all(by.xpath(locator.xpath)).getAttribute("textContent");
}

Related

AssertionError: Timed out retrying after 4000ms: Expected to find element: `//*[#id="nav-link-accountList-nav-line-1"]`, but never found it

LoginPage.js
class Login{
elements =
verifyUserName(verifyUserName){
this.elements.verifyLogin().should('have.text',verifyUserName);
}
}
//export default Login;
export default new Login();
LoginTest.cy.js
import Login from "../PageObjects/LoginPage";
describe('Page Object Model Pattern in Cypress', () => {
beforeEach(() => {
cy.visit('/')
});
it('Should Login to Home Page Test', () => {
cy.fixture('testData').then((data) => {
Login.verifyUserName(data.expectedusername)
})
})
})
HTML of the element-
<span id="nav-link-accountList-nav-line-1" class="nav-line-1 nav-progressive-content">Hello, S*****N</span>
When I'm trying to run these two files in cypress getting assertion error
"assertexpected <span#nav-link-accountList-nav-line-1.nav-line-1.nav-progressive-content> to have text Hello, S****N".
Basically it's fetching the id & class and asserting with the expected text. Can anyone please suggest any solutions? TIAyour text
I understood that you need to grab text of an element and assert that grabbed text is equal to some expected text.
//grabbed element via Id and saved into $ele using .then command
cy.get("#nav-link-accountList-nav-line-1").then(($ele) => {
//grabbed text value of the element in const txtvalue
const txtValue = $ele.text()
//did chai assertion to be equal to expected text
expect(txtValue).to.be.eq("Hello, S*****N")}

Cypress | check that search keywords match the value of any td of the displayed trs

I'm testing a search functionality that filters the entered characters against users' Firstname, Lastname, role, or email.
I have a table with multiple tr and each tr has multiple td.
I want to assert that the returned rows contain a value in any td that matches the entered keyword.
I created the below helper function but cy.get('td').contains(searchKeyword); causes the browser to hang up. any idea what could be the solution here.
assertCorrectFilterResults(searchKeyword: string) {
this.elements.tableBody().find("tr").then(rows => {
rows.toArray().forEach(row => {
cy.get('td').contains(searchKeyword);
})
});
};
My helper was inspired from the solution mentioned here How to get the total number of Rows in a table | Cypress
Table Structure
In case if you directly want to look into the td elements you can directly loop over them.
For exact Match with the searchKeyword
cy.get('tbody.MuiTableBody-root tr td').each(($ele) => {
if($ele.text().trim() == searchKeyword){
expect($ele.text().trim()).to.equal(searchKeyword) //Assertion for exact text
}
})
For partial match with the searchKeyword
cy.get('tbody.MuiTableBody-root tr td').each(($ele) => {
if($ele.text().trim().includes(searchKeyword)){
expect($ele.text().trim()).to.include(searchKeyword) //Assertion for partial text
}
})
Try wrapping each row.
This will test that all the rows contain the searchword.
assertCorrectFilterResults(searchKeyword: string) {
cy.get('tbody').find("tr").then(rows => {
rows.toArray().forEach(row => {
cy.wrap(row).contains(searchKeyword); // test that one of child <td> has searchKeyword
})
});
};
You can assert that a parent element "contains" text that is inside a child element, so
<tr>
<td></td>
<td></td>
<td>Find me!</td>
</tr>
asserting on <tr> with .contains(...) will check all the <td>.
Additional Note
When you do cy.get('td') inside the forEach, Cypress actually searches from the cy.root() element (by default it's the <body> element).
So another way to do it is to change the root element using .within()
assertCorrectFilterResults(searchKeyword: string) {
cy.get('tbody').find("tr").then(rows => {
rows.toArray().forEach(row => {
cy.wrap(row).within(() => {
cy.get('td').contains(searchKeyword); // now get works only in this row
})
})
});
};
Triggering a button in another <td> of the row:
cy.get('tbody.MuiTableBody-root tr') // note - end this selector at tr
.each(($tr, rowIndex) => { // row index is passed as 2nd param
if ($tr.text().includes(searchKeyword)) {
cy.wrap($tr).within(() => {
cy.get('td').eq(3) // for example 4th col has button
.find('button')
.click()
})
}
})

How to assert a value within a span?

How to assert a dynamic text value within a span using Within assertion method? I would like to check that value
<span class="FilterHeader--results--count">
78
</span>
is within some range (for example 0 to 100).
Use the addCustomDOMProperties method for your case. It is better than another solution because in this case the smart assertion query mechanism will work correctly.
import { Selector } from 'testcafe';
fixture `fixture`;
test('test', async t => {
const span = Selector('.FilterHeader--results--count').addCustomDOMProperties({
integerSpanValue: el => parseInt(el.innerText, 10)
});
await t.expect(span.integerSpanValue).within(0, 100);
});
Convert the text in the span to a number, and then assert that the value is between 0 and 100 (inclusive). There's also a similar example under the Within section in the documentation.
await t
.expect(parseInt(await Selector('span.FilterHeader--results--count').innerText)).within(0,100);

How do I iterate over each element in an HTML list when testing with WebdriverIO and Mocha + Chai?

I am trying to assert that each item in a certain <ol> element exists. The closest solution I can find on SO and elsewhere is to use .elements, yet this still does not work. The length of the <ol> element varies on different pages, so it cannot be hardcoded into the test. See my current test below, which should fail at i.should.not.exist but passes.
it('category hierarchy navigation test',function() {
return client
.url(Page.url)
.then(function() {
ItemPage.categoryHierarchy.should.exist;
})
.then(function() {
return client.elements(ItemPage.categoryHierarchy, function(err, res){
for (let i in res.value) {
i.should.not.exist;
}
})
})
})
What is the proper way of getting an array of and iterating over the <li> elements inside an <ol>?

Pass data-attribute value of clicked element to ajax settings

For an implementation of Magnific Popup, I need to pass a post id to the ajax settings. The post id is stored in a data attribute of the element to which Magnific Popup is bound. I would like this to work:
html element:
<a data-id="412">Clicke me</a>
Javascript:
$('.element a').magnificPopup({
type: 'ajax',
ajax: {
settings: {
url: php_array.admin_ajax,
type: 'POST',
data: ({
action:'theme_post_example',
id: postId
})
}
}
});
Where postId is read from the data attribute.
Thanks in advance.
$('.element a').magnificPopup({
callbacks: {
elementParse: function(item){
postData = {
action :'theme_post_example',
id : $(item.el[0]).attr('data-id')
}
var mp = $.magnificPopup.instance;
mp.st.ajax.settings.data = postData;
}
},
type: 'ajax',
ajax: {
settings: {
url: php_array.admin_ajax,
type: 'POST'
}
}
});
Here is how to do it:
html:
<a class="modal" data-id="412" data-action="theme_post_example">Click me</a>
jquery:
$('a.modal').magnificPopup({
type: 'ajax',
ajax: {
settings: {
url : php_array.admin_ajax,
dataType : 'json'
}
},
callbacks: {
elementParse: function() {
this.st.ajax.settings.data = {
action : this.st.el.attr('data-action'),
id : this.st.el.attr('data-id')
}
}
},
parseAjax: function( response )
{
response.data = response.data.html;
}
});
php
function theme_post_example()
{
$id = isset( $_GET['id'] ) ? $_GET['id'] : false;
$html = '<div class="white-popup mfp-with-anim">';
/**
* generate your $html code here ...
*/
$html .= '</div>';
echo json_encode( array( "html" => $html ) );
die();
}
As this answer was the original question regarding inserting data into Magnific's ajax call, I'll post this here.
After many hours of trying to figure this out, you should know that if you're using a gallery with the ability to move between gallery items without closing the popup, using elementParse to set your AJAX data will fail when you visit an item after already viewing it (while the popup is still open).
This is because elementParse is wrapped up in a check that it makes detect if an item has already been 'parsed'. Here's a small explanation as to what happens:
Open gallery at item index 2.
Item has not been parsed yet, so it sets the parsed flag to true and runs the elementParse callback (in that order). Your callback sets the ajax options to fetch this item's data, all is well.
Move (right) to item index 3.
Same as above. The item has not been parsed, so it runs the callback. Your callback sets the data. It works.
Move (left) back to item index 2.
This time the item has been parsed. It skips re-parsing the item's element for assumed potential performance reasons.Your callback is not executed. Magnific's ajax data settings will remain the same as if it were item index 3.
The AJAX call is executed with the old settings, it returns with item index 3's data instead, which is rendered to the user. Magnific will believe it is on index 2, but it is rendering index 3's data.
To resolve this, you need to hook onto a callback which is always executed pre-ajax call, like beforeChange.
The main difference is that the current item isn't passed through into the callback. Fortunately, at this point, magnific has updated their pointers to the correct index. You need to fetch the current item's element by using:
var data = {}; // Your key-value data object for jQuery's $.ajax call.
// For non-closures, you can reference mfp's instance using
// $.magnificPopup.instance instead of 'this'.
// e.g.
// var mfp = $.magnificPopup.instance;
// var itemElement = mfp.items[mfp.index].el;
var itemElement = this.items[this.index].el;
// Set the ajax data settings directly.
if(typeof this.st.ajax.settings !== 'object') {
this.st.ajax.settings = {};
}
this.st.ajax.settings.data = data;
This answer can also be used as a suitable alternative to the currently highest voted, as it will work either way.
You may use open public method to open popup dynamically http://dimsemenov.com/plugins/magnific-popup/documentation.html#public_methods
postId = $(this).attr('data-id')
$(this) retrieve the current element (the link you clicked on), and attr the value of the specified attribute.