Cypress: Typing input element value (text) to log - cypress

I've got an input field whose text contents I need to verify. Now, verifying it works fine. Bug for some reason, I cannot for the life of me manage to type the value to cy.log(). I've done it before, but I'm unable to find the example. And I've been trying all variants of invoke('val'), 'value' and 'text', to no avail.
cy.get('[data-e2e-selector=byggrad]').eq(0)
.within(() => {
cy.get('[data-e2e-selector=adresserad]').eq(0)
.within(() => {
cy.get('[data-e2e-selector=etasjerad]').eq(0)
.within(() => {
cy.get('[data-e2e-selector=finansieringsobjektrad]').eq(boligrad)
.within(() => {
cy.get('input[data-e2e-selector=boligbetegnelse]').should('have.value', '123');
cy.log('BOLIGBETEGNELSE: ' + cy.get('input[data-e2e-selector=boligbetegnelse]').invoke('val'));
//cy.log('BOLIGBETEGNELSE: ' + cy.get('input[data-e2e-selector=boligbetegnelse]').invoke('value'));
//cy.log('BOLIGBETEGNELSE: ' + cy.get('input[data-e2e-selector=boligbetegnelse]').invoke('text'));
});
});
});
});
Here's the cypress output:
The complete markup:
<input _ngcontent-pxm-c10="" class="hb-inputfelt hb-inputfelt--10tegn ng-pristine ng-valid ng-touched" data-e2e-selector="boligbetegnelse" ng-reflect-form="[object Object]" id="finansieringsobjekt-boligbetegnelse-319-H02-0">

You need to chain off of the .invoke() call to get the value.
e.g.:
cy.get("matcher").invoke("val").then((value) => {
cy.log(value);
});

Since value it's an attribute of an input element.
You can find all properties for element: devtools -> find element in DOM -> right hand side find tab properties -> scroll down and find value property
cy.get("input[data-e2e-selector=boligbetegnelse]").invoke('prop','value').then((value) => {
cy.log(value);
});

Related

Unable to get text from element when calling to it 2nd time in Cypress

I am trying to get the value of the element, then click another element and see how that value changes. I have no problem getting the value the first time, but when I try to get it after I've clicked another one, the value returns as "-" or an empty string even though the method is identical to the one that returned the correct value the first time. Here's the code.
it('select/unselect services and verify price change', () => {
cy.get('[data-qa="subtotal"]')
.invoke('text')
.then((originalSubtotal) => {
cy.log(originalSubtotal) //returns "$xxx.xx" string #1 here
//get the TPMS price
cy.get('.summary-line')
.invoke('text')
.then((tpmsText) => {
cy.log(tpmsText) //returns "$xxx.xx" string #2
//click TPMS checkbox
cy.get('.icon-checkmark')
.click()
.then(()=> {
cy.get('[data-qa="subtotal"]')
.invoke('text')
.then((newSubtotal) => {
cy.log(newSubtotal) //returns empty string or "-" even though the code is identical to what returned string #1
})
})
})
})
})
If you are not comparing values then you write all statements in sequence rather than writing it inside then
it('select/unselect services and verify price change', () => {
cy.get('[data-qa="subtotal"]')
.invoke('text')
.then((originalSubtotal) => {
cy.log(originalSubtotal) //returns "$xxx.xx" string #1 here
})
//get the TPMS price
cy.get('.summary-line')
.invoke('text')
.then((tpmsText) => {
cy.log(tpmsText) //returns "$xxx.xx" string #2
})
//click TPMS checkbox
cy.get('.icon-checkmark').click()
cy.get('[data-qa="subtotal"]')
.invoke('text')
.then((newSubtotal) => {
cy.log(newSubtotal)
})
})
The problem turned out to be that the 2nd value check happened too quickly after the click. Adding a 2 sec wait between the click and the value check solved the issue.
If you're checking for a change, using should() will be better than cy.wait(2000).
It's always better to assert with an retry than to cy.log().
cy.get('[data-qa="subtotal"]')
.invoke('text')
.then((originalSubtotal) => {
cy.get('.icon-checkmark').click() //click TPMS checkbox
cy.get('[data-qa="subtotal"]')
.invoke('text')
.should('not.eq', originalSubtotal) // up to 4 seconds retry
})

cypress each method get a child elements href

I'm using an each method in cypress to find a list of li elements.
What I'm after is to find a child inside the li and test its href value.
The child is an a tag.
Here is what I have so far:
it("should have ..... ", () => {
cy.get("ul > li")
.each((el, index, list) => {
expect(el.text()).to.be.oneOf(["hello", "world"]);
const a = cy.wrap(el).find("a");
expect(a.attr("href")).to.be.oneOf(["/hello", "/world"]);
})
.then((list) => {
expect(list).to.have.length(2);
});
});
Error: a.attr is not a function
Don't wrap the el. If you do, it becomes a Cypress command which is always async.
You would have to do
cy.wrap(el).find("a").then(a => {
expect(a.attr("href")).to.be.oneOf(["/hello", "/world"]);
})
But find works for jQuery as well.
const a = el.find("a")
expect(a.attr("href")).to.be.oneOf(["/hello", "/world"]);
The problem is you're storing the results of a Cypress (Promise-like) Chainable. Also using an expect inside each won't benefit from the retry-ability provided by should.
it("should have ..... ", () => {
cy.get("ul > li")
.should("have.length", 2)
.each(($el) => {
cy.wrap($el)
.invoke("text")
.should("be.oneOf", ["hello", "world"])
cy.wrap($el)
.find("a")
.invoke("attr", "href")
.should("be.oneOf", ["/hello", "/world"])
});
});

Return value from nested wrap

I am starting out with cypress and I have a doubt about returning a value form a custom command.
I have multiple tables across my application, and in my tables I can click a row that will open a modal with more deailed information. So I want to build a command to extract the values of a specific row, so I can store them and then compare with the modal values.
I'm also trying to do this command in a way to reuse across the different tables. However I am having issues with my return value. This is my current command:
Cypress.Commands.add(
'getRowInformation',
(rowsSelector, compareValue, mainProperty, nestedSelector) => {
let rowNumber = -1
const propertiesObject = {}
/**
* get all the field in the all the rows that might contain the compareValue
*/
cy.get(`[data-testid="${mainProperty}"]`).then($elements => {
cy.wrap($elements)
.each(($elementField, index) => {
/**
* Find first match and get the row index
*/
if (rowNumber === -1 && $elementField.text() === compareValue) {
rowNumber = index + 1
}
})
.then(() => {
/**
* Access needed row
*/
rowsSelector()
.eq(rowNumber)
.within(() => {
cy.get(nestedSelector).then($property => {
cy.wrap($property)
.each($prop => {
Object.assign(propertiesObject, { [$prop.attr('data-testid')]: $prop.text() })
})
.then(() => {
/**
* Return key value map, where key in data-testid
* and value is the element's text
*/
return cy.wrap(propertiesObject)
})
})
})
})
})
},
)
And I am calling this command in my it() as:
cy.getRowInformation(myCustomSelector, 'Compare value', 'testid', 'span').then(properties => {
console.log('properties', properties)
expect(true).to.be.true
})
My custom selector:
myCustomSelector: () => cy.get('[data-testid="row"]'),
My problem is that what gets to my .then in my it() is the rowsSelector().eq(rowNumber) and what I needed is the created propertiesObject. From the docs I could not get an example as nested as this, so do you guys think this is doable?
Docs say .within() yields the same subject it was given from the previous command.
So it seems you have to move your cy.wrap(propertiesObject) out of the within callback and put it in the outer then
You can also sub in .find() which is syntactically equivalent to .within()
rowsSelector()
.eq(rowNumber)
.find(nestedSelector).each($prop => {
Object.assign(propertiesObject, { [$prop.attr('data-testid')]: $prop.text() })
})
.then(() => cy.wrap(propertiesObject))
You might also want to review this Cypress, get index of th element to use it later for it's td element for the section that finds the rowNumber.
I haven't tried it, but maybe
cy.contains(`[data-testid="${mainProperty}"]`, compareValue) // only 1st match returned
.invoke('index')
.then((rowNumber) => {
rowsSelector()
.eq(rowNumber)
.find(nestedSelector)
.each($prop => {
Object.assign(propertiesObject, { [$prop.attr('data-testid')]: $prop.text() })
})
.then(() => cy.wrap(propertiesObject))
})

Cypress command that return values from DOM

In my DOM i have an input and a div, and i want to get the value of both in one command.
Here is an HTML exemple
<div id="myDiv">Content of the div</div>
<input id="myInput" value="2000" />
Here is what i tried for my command
Cypress.Commands.add("getDomValues", () => {
var divValue = cy.get('#myDiv').invoke('text')
var inputValue = cy.get('#myInput').invoke('val')
return cy.wrap({
divValue:divValue,
inputValue:inputValue
});
});
If there is no cy.wrap around my returned object i get this error
Unhandled rejection CypressError: Cypress detected that you invoked
one or more cy commands in a custom command but returned a different
value.
And then in my test for now i use it like that
cy.getDomValues().then((values)=>{
console.log(values)
})
In my console inside the returned object i have something like that for both values
$Chainer {userInvocationStack: " at Context.eval (http://localhost:8888/__cypress/tests?p=cypress/support/index.js:181:24)", specWindow: Window, chainerId: "chainer4419", firstCall: false, useInitialStack: false}
Do you have any idea how i could have a result like that ?
{
divValue:"Content of the div",
inputValue:"2000"
}
You need to access the values with .then()
Cypress.Commands.add("getDomValues", () => {
cy.get('#myDiv').invoke('text').then(divValue => {
cy.get('#myInput').invoke('val').then(inputValue => {
// no need to wrap, Cypress does it for you
return {
divValue, // short form if attribute name === variable name
inputValue
}
});
The error you received was because you were returning Chainers instead of values.
You can use .as() to assign an alias for later use.
cy.get('#myDiv').invoke('text').as('divValue')
cy.get('##myInput').invoke('val').as('inputValue')
Then use these values later separately like -
cy.get('#divValue').then(divValue => {
//Do something with div value
})
cy.get('#inputValue').then(inputValue => {
//Do something with input value
})
OR, use these values later together like -
cy.get('#divValue').then(divValue => {
cy.get('#inputValue').then(inputValue => {
//Do something with div value
//Do something with input value
})
})

Accessing text of the element using invoke() in each(). Cypress

Want to access a text from the group of the elements.
this approach doesn't work, the runner is giving an error saying that invoke is not a function
cy.get('div[class^="lalala"]')
.each(function($sec, i, $sects) {
$sec.find('header[class^="tatata"]')
.invoke('text').then((text) => {
let secText = text
cy.log(secText);
});
})
But without each() it is working when I am accessing any of the elements:
cy.get('div[class^="lalala"]').first()
.find('header[class^="tatata"]')
.invoke('text')
.then((text) => {
let secText = text
cy.log(secText);
});
})
How can I handle that?
$sec is a wrapped jQuery element and $sec.find() returns a jQuery element as well. You have to use cy.wrap to call invoke on it.
cy.get('div[class^="lalala"]').each(function ($sec, i, $sects) {
cy.wrap($sec.find('header[class^="tatata"]'))
.invoke('text')
.then((secText) => {
cy.log(secText)
})
})

Resources