So I have two values on a page that I need to compare and as per the result perform some actions.
//First Block
cy.get('selctor1').invoke('text').then(somevalue => {
cy.get('selector2').should('have.text', somevalue).then(() => {
#Do Something when equal
})
})
//Second Block
cy.get('selctor1').invoke('text').then(somevalue => {
cy.get('selector2').should('not.have.text', somevalue).then(() => {
#Do Something when not equal
})
})
So for the positive case when both values are equal everything works fine. But for the case when two values are not equal, it's only checking the first block and fails. What should I do so that it executes the second block when values are not equal and not the first block?
Sorry for not being clear the first time. Here is my edited answer:
Then vs Should:
Try to avoid then where possible. then is not repeatable and will introduce unexpected behaviour.
But also will should introduce unexpeced behaviour.
Example for a bad usage of then:
describe("asd", () => {
it("fails but retries", () =>{
console.log("######### first test")
cy.wrap({ fn: () => console.log(new Date())})
.invoke("fn")
.should(r => expect(r).to.eq(true));
})
it("fails but retries not", () =>{
console.log("######### next test")
cy.wrap({ fn: () => console.log(new Date())})
.invoke("fn")
.then(r => {
expect(r).to.eq(true)
});
})
})
In this example you see the same code twice but the first block uses should while the second block uses then. The assertion must fail but in the first block, the assertion is repeated. Open the DEV COnsole to see many retries for the first block but no retry in the second.
This is what I mean by "unexpected" behaviour. Let's say, you wrap a object that is dynamically extended (maybe by a UI action) and you are expecting a property on this object. In the second block (then) the UI acton must be executed very fast and before thethenis executed so that theexpect` does not fail.
In the should case, you have 4 seconds (in case of `defaultCommandTimeout is not overwritten) left until the assert will finally fail.
Bad usage of should:
describe("ad", () => {
it("test", () => {
cy.visit("https://www.cypress.io/")
cy.get("*[aria-label='pricing']")
.invoke('text').should(someValue => {
cy.get("asdad", {timeout: 5000}).should("not.exist");
})
})
})
What would you expect? A green test? No, this test fails:
Why is this the case? Because get introduces an implicit assert "should exist" (see: https://docs.cypress.io/guides/core-concepts/introduction-to-cypress.html#Default-Assertions ).
Should with callback skips the default assertion (see: https://docs.cypress.io/api/commands/should.html#Notes ).I think they skip it by toggling it by flag. This could have the effect of reversing the flag again and thus forces cypress to check if "asdad" does exist even though we use should not exist.
There is an issue for this stuff: https://github.com/cypress-io/cypress/issues/5963
I do not know why cy.log has the behaviour you mentioned in your case. So either you use then if you want to use cy commands within then callback or you avoid the usage of cy commands and use should with explicit assertions (expect). Maybe after that issue is fixed, cy.log also can be used.
Old Answer:
cy.get('selctor1').invoke('text').should(someValue => {
const $el = Cypress.$('selector2');
if ($el.text() ==== someValue) {
// positive
expect()....
} else {
// negative
expect()....
}
})
You can use should with a callback. This callback (and the previous invoke command) is executed as long as the timeout is reached or no assertion fails.
You always can use the raw jQuery object to work with. This depends on whether or not you need all the checks cypress is executing during a get().
Please let me know if you need further assistance.
Related
I am writing Cypress tests for an application that has a dynamic group/list of items. Each item has a details link that when clicked creates a popup with said details. I need to close that popup and move to the next item in the group.
I first tried to use the .each() command on the group and then would .click({multiple:true}) the details but the popup would cover the the next click. Adding {foce:true} does allow all of the popups to display but I don't think that is in the spirit of how the application should function.
My latest attempt has been to create a custom command using .next() to iterate through the group. This works but when .next() reaches the end of the group it yields "" and so the test ultimately fails.
The actual error I get is:
Expected to find element: ``, but never found it. Queried from element: <div.groups.ng-star-inserted>
the .spec.ts
describe('Can select incentives and view details', () => {
it('Views incentive details', () => {
cy.optionPop('section#Incentives div.groups:first')
})
})
the index.ts
Cypress.Commands.add('optionPop', (clickable) => {
cy.get(clickable).find('[ng-reflect-track="Estimator, open_selection_dial"]').click()
cy.get('mat-dialog-container i.close').click()
cy.get(clickable).next().as('clicked').then(($clicked) => {
//fails at .next ^ because '' is yielded at end of list
cy.wrap($clicked).should('not.eq','')
})
cy.optionPop('#clicked')
})
You basically have the right idea, but it might work better in a plain JS function rather than a custom command.
function openPopups(clickables) {
if (clickables.length === 0) return // exit when array is empty
const clickable = clickables.pop() // extract one and reduce array
cy.wrap(clickable)
.find('[ng-reflect-track="Estimator, open_selection_dial"]').click()
cy.get('mat-dialog-container i.close')
.should('be.visible') // in case popup is slow
.click()
// wait for this popup to go, then proceed to next
cy.get('mat-dialog-container')
.should('not.be.visible')
.then(() => {
openPopups(clickables) // clickables now has one less item
})
}
cy.get('section#Incentives div.groups') // get all the popups
.then($popups => {
const popupsArray = Array.from($popups) // convert jQuery result to array
openPopups(popupsArray)
})
Some extra notes:
Using Array.from($popups) because we don't know how many in the list, and want to use array.pop() to grab each item and at the same time reduce the array (it's length will control the loop exit).
clickables is a list of raw elements, so cy.wrap(clickable) makes the individual element usable with Cypress commands like .find()
.should('be.visible') - when dealing with popup, the DOM is often altered by the click event that opens it, which can be slow relative to the speed the test runs at. Adding .should('be.visible') is a guard to make sure the test is not flaky on some runs (e.g if using CI)
.should('not.be.visible').then(() => ... - since you have some problems with multiple overlapping popups this will ensure each popup has gone before testing the next one.
okay,
here is the situation:
On a test I'm doing on a website, it always has a table, but when that table has no elements it is hidden. I need to perform an action only if that table has visible elements. And if not, just continue to the next tests.
So, if for example, the table is empty, and I write this --- cy.get('element', {timeout: 60000}).should('be.visible') --- The test times out, which is correct, since the table is empty and therefore wont become visible.
But, of course, I need the test NOT TO timeout, I need that after the stipulated time has passed, it continues to the next tests.
So, I thought of this:
cy.get('element').then(($table) => {
if ($table.is(':visible')){
cy.log('JUST TESTING')
}
})
Problem is, it always enter the if and print the console log JUST TESTING. That means that the if visible condition is not working.
Any ideas?
Thanks!
So you have two cases to test, one if the table has elements and one if the table has no elements. I would split this into two test cases and populate/not populate the table so it either shows or doesn't show. IMHO, doing this in one test case doesn't prove the the table is working or not working as how do you know what you are seeing is correct. You could have the situation where the table should be showing as it has content, but doesn't, but you're test will always pass as it doesn't know what the state of the table should be. So something like...
describe('Testing my table', () => {
context('table is populated', () => {
beforeEach {
// populate the table with data
}
it('should show the table, () => {
// Some testing stuff in here to check the table is showing
});
context('table is NOT populated', () => {
beforeEach {
// Any set up you need for a non-populated table
}
it('shouldn't show the table, () => {
// Check we can't see the table
});
What are the possible reasons for query being stuck on loading = true (networkStatus = 1)?
I cannot get a query result on refetch and cannot log 'called2'
graphql(_stepQuery, {
name: 'stepQuery',
options: ({goalDocId}) => ({
fetchPolicy: 'network-only',
notifyOnNetworkStatusChange: true,
variables: {
goalDocId
}
})
}
)
componentWillReceiveProps(nextProps) {
let stepIdsFromServer
if (nextProps.currentGoalSteps.length > this.props.currentGoalSteps.length) {
console.log('called')
this.props.stepQuery.refetch()
console.log('this.props', this.props)
console.log('nextProps',nextProps)
if (!nextProps.stepQuery.loading) {
// console.log('nextProps.stepQuery.allSteps', nextProps.stepQuery.allSteps)
console.log('called2')
}
This looks quite dangerous for a infinite loop.
First the refetch function is a Promise, so you will not be able to know the correct query state right after the call for refetching. You would need to go on in the .then function. See refetch Api.
Second the query in the end is executed inside the graphql wrapper Component. So you should not check the loading state and refetch in the componentWillReceiveProps function, Because when the query is executed again the whole component is instantiated again and will enter the componentWillReceiveProps function with resetted states and so on.
If you need some kind of search, i suggest you use a mutation as a workaround (using withApollo wrapper and in the componentWillReceiveProps you call this.props.client("MUTATION")), because this will not render the whole component.
I expected the following sample test to fail if I use toEqual(), but it passes:
it('sample test to check toEqual and toBe', () => {
const expectedAction = {test: '123', value: undefined};
const actualAction = {test: '123'};
expect(expectedAction).toEqual(actualAction);
})
The same test fails if I use .toBe(). However, if I fix the test case like this:
it('sample test to check toEqual and toBe', () => {
const expectedAction = {test: '123', value: '123'};
const actualAction = {test: '123', '123'};
expect(expectedAction).toBe(actualAction);
});
it again fails saying that "Compared values have no visual difference".
Is the behaviour correct? How to modify my test case so that it fails correctly.
If you really need it to be considered as different, one option would be to use Jest snapshot test, like:
expect(expectedAction).toMatchSnapshot();
The first time, it will create a snapshot file and print the javascript object to it, when you run the test again, it will compare it to the snapshot.
Most of the times, the snapshots are used to compare the component rendered tree, but you can use it for any javascript object.
The first one passes cause toEqual compares every element of your objects with each other, so in your case
expectedAction.test === actualAction.test
as both are '123'
but the same is true for
expectedAction.value === actualAction.value
as both are undefined
The second test fails cause toBe uses === to compare the two objects which will of cause fail as they are not the same instances. The only way that an object will passes toBe would be to use itself for comparison:
expect(expectedAction).toBe(expectedAction)
I'm having difficulty grasping how to swap out a promise chain from my Rx stream so I can test it.
The snippet below is from the documentation of RxJs createResolvedPromise and has been enhanced with a chained .then(()=>..) to better simulate my code. When running this example the .then() is not applied to the results emitted.
var scheduler = new Rx.TestScheduler();
// Create resolved promise
var xs = scheduler.createResolvedPromise(201, 'bar')
.then(val => 'foo:'+val);
// Note we'll start at 200 for subscribe, hence missing the 150 mark
var res = scheduler.startScheduler(
// fromPromise needs scheduler explicitly, see https://github.com/Reactive-Extensions/RxJS/issues/976
() => Rx.Observable.fromPromise(xs, scheduler)
);
// expected: value to be foo:bar but got bar
console.log(res.messages.map(msg => `msg:#${msg.time} val:${msg.value}`));
<script src="https://cdnjs.cloudflare.com/ajax/libs/rxjs/4.1.0/rx.all.js"></script>
It seems that the .then() is ignored when using the TestScheduler (although createResolvedPromise returns a Mock promise with implementation of .then to also return a MockPromise...).
The example above is heavily simplified, The real code would be something along:
const myStream = Rx.Observable.interval(100)
.flatMap(() => doPromiseOne().then(res => doPromiseTwo(res)))
.subscribe(console.log);
So what i am trying to attain in my test is that i can resolve promiseOne (using createResolvedPromise?) and let it flow through doPromiseTwo so i can observe the results.