CYPRESS - Cant make a THEN => IF VISIBLE statement - if-statement

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
});

Related

cypress .next at end of list yields '' but I need to break instead

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.

Compare two values and make decision in Cypress

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.

apollo react: proper way to switch a query's params

In my app I have a sidebar with a list of "saved searches" and a central area that should show the results of a search. Whenever I click on a saved search link, I want to update the central area with the results of that search.
What is the proper way to do this with apollo-react?
I tried with this:
// SidebarConnector.js:
const withVideoSearch = graphql(
VIDEO_SEARCH_QUERY,
{
name: 'videoSearchQuery',
props: ({ videoSearchQuery }) => {
return ({
searchVideos: videoSearchQuery.refetch,
});
},
}
);
export default withVideoSearch(Sidebar);
My saved searches are doing a searchVideos({ query: "some query" }) on click which, based on the above, is doing a refetch for the VIDEO_SEARCH_QUERY query with different variables.
This works fine, the call is made to the graphql server and results are returned just fine.
For the main component that shows the list of results I use:
export default graphql(VIDEO_SEARCH_QUERY)(ResultList);
Initially the main component gets its results from the server as if the query was done without variables which is fine, exactly how I want it.
The problem is that every refetch seems to create a different entry in ROOT_QUERY in apollo's store and my main component is "locked" into the one without variables.
Here's what apollo's store looks like after the initial fetch and one of the refetches triggered from a saved search:
ROOT_QUERY
searchVideos({"query":"coke"}): [Video]
0:▾Video:arLaecAu5ns
searchVideos({"query":null}): [Video]
0:▾Video:3HXg-oVMA0c
So my question is how to either switch the main component to the "current search" or how to overwrite the store on every refresh so that there's only one key so the main component updates correctly.
For completeness here's my VIDEO_SEARCH_QUERY:
export const VIDEO_SEARCH_QUERY = gql`
query searchVideos($query: String) {
searchVideos(query: $query) {
...fVideo
}
}
${fVideo}
`;
Maybe I'm misunderstanding your use case, but it seems like there's no need to utilize refetch here. It would be simpler to persist whatever the selected search string is as state, pass that state down as a prop to your main component and then just use that prop as the variable in your GraphQL request. So the graphql call inside your ResultList component would look something like this:
const options = props => ({ variables: { query: props.searchString } })
export default graphql(VIDEO_SEARCH_QUERY, { options })(ResultList);
Then just have your onClick handler for each saved search set the state to whatever that search string is, and Apollo will do the rest. This is super easy with Redux -- just fire off the appropriate action. If you're not using Redux, you may have to lift the state up so it can then be passed down as a prop, but the concept is the same.

<query>.loading will not change to true

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.

Can't run more than one NgTestBed in one angular dart test

I have two test methods in one file. However when I run them the last one fails.
When I try to debug the test inside the browser after the first answer.dispatchEvent(new MouseEvent('click')); executes the event listeners on the second row won't activate as if they weren't able to listen to any click events. I tried clicking manually as well and the rows didn't change color like the first row (using the exact same code).
import 'dart:html';
import 'package:angular2/angular2.dart';
import 'package:angular_test/angular_test.dart';
import 'package:test/test.dart';
import '...multiple_choice_quiz_component.dart';
#AngularEntrypoint()
void main()
{
tearDown(disposeAnyRunningTest);
group('$MultipleChoiceQuizComponent', () {
test('should add "incorrect" css class to incorrect answer', () async {
NgTestBed bed = new NgTestBed<MultipleChoiceQuizComponent>();
NgTestFixture fixture = await bed.create();
Element answer = fixture.rootElement.querySelector('.quiz-choice:nth-child(2)');
answer.dispatchEvent(new MouseEvent('click'));
bool hasClass = answer.classes.contains('incorrect');
expect(hasClass, true);
});
test('should add "correct" css class to correct answer', () async {
NgTestBed bed = new NgTestBed<MultipleChoiceQuizComponent>();
NgTestFixture fixture = await bed.create();
Element answer = fixture.rootElement.querySelector('.quiz-choice:nth-child(3)');
answer.dispatchEvent(new MouseEvent('click'));
bool hasClass = answer.classes.contains('correct');
expect(hasClass, true);
});
});
}
Here is a screenshot.
The first row is the first test method. answer.dispatchEvent(new MouseEvent('click')); executes correctly and the color changes.
However on the second row with the exact same code the whole second row is unresponsive. Like I said it's almost as if the event listeners for the second row are disabled.
In fact, if I put the two methods in different dart test files both tests pass.
Why can't I have these two tests in one file?
There appears to be a bug where disposeAnyRunningTest doesn't actually do what we want.
https://github.com/dart-lang/angular_test/issues/46
This is why it behaves differently for you when you have the test cases in separate files, but doesn't work when they run together.
The tests might not work when they aren't properly disposed for a few reasons.
The component you are testing might have a bug which does not allow multiple copies on the page at once. Do you use element ids anywhere that might make them only work when there is a single instance?
Fixture.rootElement might have a bug which is causing it to return the element from the previous test instead of the new one.