Unable to assert a value in Cypress - cypress

I have an input field where I type the value 1000, and when I try to assert the value, the assertion fails:
https://i.stack.imgur.com/4WVhK.png
My code cy.get('#insurance_cover_money').type(1000).should('have.value', '1 000')
HTML: https://i.stack.imgur.com/0KS7b.png
Thank you!

You can try this.
cy.get('#insurance_cover_money').type('1000').should('have.value', '1000')
If the above is still not working you can do two things:
Log the value and then copy-paste it into the should assertion.
cy.get('#insurance_cover_money')
.type('1000')
.invoke('val')
.then((val) => {
cy.log(val)
})
Extract the value in the first get and then pass the same value pass into the should assertion.
cy.get('#insurance_cover_money')
.type('1000')
.should('not.have.value', '0')
.invoke('val')
.then((val) => {
cy.get('#insurance_cover_money').should('have.value', val)
})
You can also try increasing the timeout:
cy.get('#insurance_cover_money', {timeout: 8000})
.type('1000')
.should('have.value', '1000')
cy.get('#insurance_cover_money', {timeout: 8000})
.type('1000')
.should('have.value', '1 000')
In case you want to remove spaces and assert, you can do like this:
cy.get('#insurance_cover_money')
.type('1000')
.invoke('val')
.then((val) => val.replace(/\s/g, ''))
.should('eq', '1000')

So all I needed to do, is to remove the spaces inside the string, and then do the assertion:
cy.get('#insurance_cover_money').type(1000).invoke('val').then(value => {
const string = value.replace(/\s/g, '') // removes the spaces inside a string
expect(string).to.equal('1000')
})

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() .wrap() .invoke('text') takes long time

I am trying to check the items shown in my table for some assertions. My way is to put all of the items in an array and then iterate over that array.
My problem: All assertions already passed but the cypress runner still takes a lot of time to finish the cy.wrap(.invoke(text)) jobs.
Since this is a very core command of my cypress tests it would be great to have a more efficient function.
My command:
cy.get('table tbody').within(() => {
cy.get('tr').each((tr) => {
cy.wrap(tr.children().eq(index)).invoke('text').then((text) => {
text = text.trim();
arrayWithValuesOfTheList.push(text);
});
})
.then(() => {
//in here all the (quickly passing) assertions are...
});
});
Thanks for any help in advance. I appreciate you all!
You can avoid wrapping the value, will give some increase in speed but it's hard to say what is the slowest part.
const arrayWithValuesOfTheList = []
cy.get('table tbody tr')
.each($tr => {
arrayWithValuesOfTheList.push($tr.children().eq(index).text())
})
.then(() => {
//in here all the (quickly passing) assertions are...
})
})
You can do something like this. It gets the tr values one by one and matches in against a regex pattern.
cy.get('table tbody tr').each(($ele) => {
cy.wrap($ele.text().trim())
.should('match', /myregexp/)
.and('not.include', 'some text')
})
If you want to assert on individual cells, using .each($cell => {...}) is fine but if you want whole-column assertions (e.g sorted, unique-values) it gets difficult with .each().
To build something adaptable for various tests, take a look at the pattern here Sorting the table.
The idea is to create helper functions using .map() to selected table rows and columns.
const { _ } = Cypress
// helpers, reusable
const getColumn = (colIndex) => {
return (rows$) => {
const children$ = _.map(rows$, 'children')
return _.map(children$, `[${colIndex}]`)
}
}
const toStrings = (cells$) => _.map(cells$, 'textContent')
const toNumbers = (texts) => _.map(text, Number)
cy.get('table tbody tr') // rows of table
.then(getColumn(1)) // extract 2nd column
.then(toStrings) // get the text value
.then(toNumbers) // convert if assertions require numeric values
// whole-column assertion example
.should(values => {
const sorted = _.sortBy(values)
expect(values, 'cells are sorted 📈').to.deep.equal(sorted)
})
// individual value assertion
.should(values => {
values.forEach(value => {
expect(value).to.be.lt(100)
})
})
Addressing performance issue
If performance is poor, you can reduce the number of process steps at the Cypress command level by using jQuery-only commands.
This will avoid adding commands to the Cypress queue which is likely to be the slowest part
const arrayWithValuesOfTheList = []
cy.get('table tbody tr td:nth-child(2)') // 2nd col of each row
.then($tds => { // only jQuery methods inside
$tds.text((index, cellText) => {
arrayWithValuesOfTheList.push(cellText.trim())
})
})
.then(() => {
//in here all the (quickly passing) assertions are...
})
})

Cypress test validation

I have function to convert text to Uppercase, what i want to do is to write test in cypress for the fonction and print the result in htmml
here the function :
module.exports = () => ({
upperCaseName: (name) => {
return name.toUpperCase()
}
});
here i print it :
<h1 cy-data='uppercase'> the result </h1>
so how i should write the test :
i know i could do this :
cy.get('[cy-data=uppercase]').contains('the result')
but i want somthing like this :
example:
cy.get('[cy-data=uppercase]').to.be.upperCase
is it possible?
How about
cy.get('[cy-data=uppercase]').contains('THE RESULT', { matchCase: true })
but { matchCase: true } is the default setting, so can be just
cy.get('[cy-data=uppercase]').contains('THE RESULT')
Custom Chai assertion for uppercase
window.chai.Assertion.addProperty('uppercase', function () {
var obj = this._obj;
new chai.Assertion(obj).to.be.a('string');
this.assert(
obj === obj.toUpperCase()
, 'expected #{this} to be all uppercase' // error message when fail for normal
, 'expected #{this} to not be all uppercase' // error message when fail for negated
);
});
it('test for upper case (and not uppercase)', () => {
cy.get('[cy-data=uppercase]').invoke('text').should('be.uppercase')
cy.get('[cy-data=lowercase]').invoke('text').should('not.be.uppercase')
})
Extends internal Cypress version of Chai with new assertion, works in .should() with retry and timeout as well.
Or without custom chai assertion
it('test for upper case (and not uppercase)', () => {
cy.get('[cy-data=uppercase]').invoke('text')
.should(text => expect(text).to.eq(text.toUpperCase())
cy.get('[cy-data=lowercase]').invoke('text')
.should(text => expect(text).not.to.eq(text.toUpperCase())
})
You can use regex as well to check that the text has uppercase or not.
cy.get('[cy-data=uppercase]')
.invoke('text')
.should('match', /\b[A-Z]+\b/)
To check if everything in the sentence is in uppercase along with special characters you can use the regex ^[^a-z]*$
cy.get('[cy-data=uppercase]')
.invoke('text')
.should('match', /^[^a-z]*$/)
You can play around with regex as per your requirement.

Cypress how to get a text from a div and store in a variable for later

I am new to Cypress and some of the things that I expect to work have really weird issues.
For example, I am trying to get the value of a column in a table and use the value in a search input. I have done it like this:
it('should filter', () => {
let id = 0;
cy.get('[data-cy=data-table-row]').should('have.length', 25);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
id = $div1.text();
expect(id).not.to.eq(0);
});
//expect(id).not.to.eq(0);
cy.get('[data-cy=table-filters-search]').find('input').type(id);
cy.get('[data-cy=data-table-row]').should('have.length', 1);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
expect(id).to.eq($div1.text());
});
});
But when I run this, I get an error stating that [data-cy=data-table-row] has a length of 25 not 1.
It turns out that the id variable I am using is not accessible outside the should method. I assume that's because it is a promise.
If I try to do this:
it('should filter', () => {
let id = 0;
cy.get('[data-cy=data-table-row]').should('have.length', 25);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
id = $div1.text();
expect(id).not.to.eq(0);
cy.get('[data-cy=table-filters-search]').find('input').type(id);
cy.get('[data-cy=data-table-row]').should('have.length', 1);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').should(($div1) => {
expect(id).to.eq($div1.text());
});
});
});
The test goes mental and tries to get the [data-cy=table-filters-search] over and over and over again.
I am not sure why.
Is there an easier way to grab the innerText of a div and store it to compare later?
As someone gave a response, I tried this:
it('should filter', () => {
let variables = {};
cy.get('[data-cy=data-table-row]').should('have.length', 25);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').then(($div1) => {
variables.id = $div1.text();
expect(variables.id).not.to.be.undefined;
});
console.log(variables);
expect(variables.id).not.to.be.undefined;
cy.get('[data-cy=table-filters-search]').find('input').type(variables.id);
cy.get('[data-cy=data-table-row]').should('have.length', 1);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id').then(($div1) => {
expect(variables.id).to.eq($div1.text());
});
});
But the test fails on the second expect(variables.id).not.to.be.undefined;
Closure problem
The problem with the first example is that the test runs in two phases. The first phase sets up the commands in the queue, and the second runs them.
During the first phase, .type(id) "captures" the current value of id (which is "0") and in the second phase that's the value that gets used.
You can fix it in a couple of ways, with an alias or moving the type(id) inside the callback, as per your second example.
This gets around the closure problem by deferring cy.get(...).find('input').type(id) until the id has actually changed.
Retry problem
The problem with the second example is that should() with an expect in the callback will retry until it succeeds or times out.
Something in the callback is continuously failing (or an error is thrown) causing a continuous retry. It should time out, not sure why that doesn't happen.
You can separate the parts of the callback into two sections, and use a .then() which does not attempt to retry.
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
.should(($div1) => {
id = +$div1.text(); // Note $div1.text() is always a string
// so convert with "+" to compare numbers
expect(id).not.to.eq(0) // otherwise this always succeeds
})
.then(($div1) => { // use a then which does not retry
id = $div1.text();
cy.get('[data-cy=table-filters-search]').find('input').type(id);
cy.get('[data-cy=data-table-row]').should('have.length', 1);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
.should(($div1) => {
expect(id).to.eq($div1.text())
});
})
Or
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
.invoke('text') // find it's text
.should('not.eq', '0') // passes on the id
.then(id => {
cy.get('[data-cy=table-filters-search]').find('input').type(id);
cy.get('[data-cy=data-table-row]').should('have.length', 1);
cy.get('[data-cy=data-table-row]:nth-child(1) > .cdk-column-id')
.should($div1 => expect(id).to.eq($div1.text()) );
})
If you want to use it within a single case - Allias is the cypress way:
cy.wrap('value').as('variable') //setting the variable
cy.get('#variable') // with this you can call it at any time in the test after aliasing
.then(variable =>{
//here you can use it
})
For a variable to use on multiple cases I recommend using an object as a Reference
let variables = {} //This would need to be declared on the scope you wish to use it in
cy.get('element')
.then($el => {
variables.elText = $el.text() //Assigning the value
})
cy.log(variables.elText) //You can call it later like this

Ignore switchMap return value

I want to resolve an observable but I don't want the return value to replace the previous value in the pipe. Is there any asynchronous tap()? I need an operator like a switchMap but I want to ignore the return.
of(1).pipe(switchMap(() => of(2))).subscribe(console.log); // expected: 1
I could create a custom operator but sure there's something built-in in rxjs.
I ended up with this custom operator. It is like tap but resolves observables (and should be updated to also support promises).
export function switchTap<T, R>(next: (x: T) => Observable<R>): MonoTypeOperatorFunction<T>;
export function switchTap<R>(observable: Observable<R>): MonoTypeOperatorFunction<R>;
export function switchTap<T, R>(
arg: Observable<T> | ((x: T) => Observable<R>)
): MonoTypeOperatorFunction<T> {
const next: (x: any) => Observable<T | R> =
typeof arg === 'function' ? arg : (x: any): Observable<T> => arg;
return switchMap<T, T>(value => next(value).pipe(ignoreElements(), concat(of(value))));
}
Usage:
of(1).pipe(switchTap(of(2))).subscribe(console.log) // 1
or with a function:
of(1)
.pipe(
switchTap(value => {
console.log(value); // value: 1
return of(value + 1);
})
)
.subscribe(console.log); // 1
If you just want to simply ignore the values of the subscribe, then just don't pass in any arguments in the subscribe callback:
of(1).pipe(switchMap(() => of(2))).subscribe(()=>{
console.log('no arguments')
});
If you however want to retain the values of the first observable, things can get tricky. One way is to use Subject to retain the value:
//create a BehaviorSubject
var cache = new BehaviorSubject<any>(0);
of(1).pipe(switchMap((first) => {
cache.next(first);
return of(2);
})).subscribe(() => {
console.log(cache.value) //gives 1
});
Or you can use .map() to alter the values. This is kind of hacky and the code is harder to maintain:
of(1).pipe(switchMap((first) => {
return of(2).map(() => first);
})).subscribe((second) => {
console.log(second) //gives 1 because the values was mapped
});
I do it like so
of(2).pipe(
switchMap( num => this.doSmtg(num), num => num)
).subscribe(num => console.log(num)); // 2
Second param of switchmap receives two value the one passed to this.doSmtg and the value returned by doSmtg(num)'s observable.
For anyone new having the same problem I would advise using the resultSelector parameter supported by switchMap and other RxJS mapping operators.
Example:
switchMap(1 => of(2), (one, two) => one)
For further reading: https://www.learnrxjs.io/operators/transformation/mergemap.html
I think you could use delayWhen operator to achieve a similar functionality.
of(1)
.pipe(
delayWhen(value => {
console.log(value); // value: 1
return of(value + 1);
})
).subscribe(console.log); // 1

Resources