Go to next page until element found - cypress

I'm using Cypress for my automated tests. I'm trying to find a product on a page and click on it. If the product not displayed on the page, go to the next one until it is found.
I've been trying a lot of things already: while loop, each loop, simple cy.get but none of them work. Can anyone help me to solve this?

You'll need a recursive command, implementation of which will depend on your specific scenario. There's no one-size-fits-all solution, but generally it will look something like this:
function findElem ( targetSelector ) {
// first, we need to query a container (could be table, or a generic item
// container). The important thing is, this container must exist on every
// page you'll traverse.
cy.get('.someContainer').then( $container => {
// synchronously find the target element, however you want. In this
// example I use an attribute selector, but you can do whatever you
// want.
if ( $container.find(targetSelector).length ) {
return true;
} else {
return false;
}
}).then( found => {
if ( found ) {
return cy.log(`found elem "${targetSelector}"`);
} else {
// synchronously check if there's a next page/table
if ( Cypress.$('.nextPageButton').length ) {
// when know that there is a next page, click it using cypress
cy.get('.nextPageButton').click();
// here, assert that next page/table is loaded, possibly by
// asserting that current page/table is removed from DOM
// then, recurse
return findElem(targetSelector);
} else {
throw new Error(`Couldn't find elem "${targetSelector}" on any page`);
}
}
});
}
it('test', () => {
findElem(`[data-id="42"]`);
});
The crux of the solution is using a combination of commands to query a container, and synchronous inspection (using jQuery or whatever) to find the actual element. Learn more at Conditional Testing.
For a concrete implementation, you can refer to an older answer I gave to a similar question.

Related

I want to use assertion for the checkbox

I want to use assertion for this checkbox. It depends on duration. If it's checked duration = forever, if not = a month.
cy.wrap(cy.get('span.ant-checkbox').should('have.class','ant-checkbox-checked')).then((a) => {
if a == true {
cy.log('Forever')
}
})
A few thoughts:
You don't need to cy.wrap() your entire statement. cy.wrap() would primarily be used to wrap a JQuery yielded from cy.get() or a similar command, and insert it back into the Cypress command chain.
Your assertion that the element has a certain class will fail and stop your test before even getting to the .then() part of the command if the element does not have the ant-checkbox-checked class.
Instead, if we get the element, we can use JQuery functions (in this case, .hasClass())to determine if it has the class we want.
cy.get('span.ant-checkbox').then(($el) => {
// cy.get yields a JQuery<HTMLElement>
if ($el.hasClass('ant-checkbox-checked')) {
cy.log('Forever');
} else {
cy.log('A month');
}
});

How to proper Conditional Testing

I just want to ask how to properly conditional testing? I have this code here
cy.get('[data-slug="add-to-any"] > .plugin-title > strong').then(($slug) => {
if (expect($slug).not.to.exist){
//It will passed
}else if (expect($slug).to.exist){
cy.get('#deactivate-add-to-any').should('not.exist')
}
I assert the element to not.to.exist, but it gives me this error
Expected to find element: [data-slug="add-to-any"] > .plugin-title > strong, but never found it.
I am really lost what assertions I need to use.
The ideal way (if it works in your scenario) is to shift the last selector inside the .then()
cy.get('[data-slug="add-to-any"] > .plugin-title')
.then(($pluginTitle) => {
const $slug = $pluginTitle.find('strong'); // this finds with jQuery
// which won't fail the test
// if not found
if ($slug.length === 0) { // not found
} else {
cy.get('#deactivate-add-to-any').should('not.exist')
}
})
It's not 100% fool-proof, if $slug is loaded asynchronously (say via fetch) it won't be there immediately and the test might pass when in fact the $slug turns up 100 ms after the test runs.
You need to understand the way the app works to really be sure.
Cypress docs show this pattern, using <body> as the "stable" element (always present after page load).
cy.get('body').then($body => {
const slug = $body.find('[data-slug="add-to-any"] > .plugin-title > strong')
if ($slug.length) {
...
It's less than ideal because the page might have <body> but still be fetching elements inside it.
Best practice IMO is to try the immediate parent element of the conditional one. If that is also conditional, move up the element tree until you find an element that is stable/present at that point in you test.
Or add a guard condition that waits for page fetch to complete. A cy.intercept() is useful for that, or even just this
cy.get('[data-slug="add-to-any"] > .plugin-title')
.should('be.visible') // retries until .plugin-title is showing
.then(($pluginTitle) => {
const $slug = $pluginTitle.find('strong')
if ($slug.length === 0) {
...
Simple example
cy.get("body").then($body => {
if ($body.find('[data-slug="add-to-any"] > .plugin-title').length > 0) {
cy.get('[data-slug="add-to-any"] > .plugin-title').then($title => {
if ($title.is(':visible')){
//you get here only if it EXISTS and is VISIBLE
}
});
} else {
//you get here if the it DOESN'T EXIST
cy.get('#deactivate-add-to-any').should('not.exist')
}
});

How to efficiently remove first element from a selection?

I have a page that displays some data using d3.js. Due to the heavy processing load, when the page load it freezes the browser for a few seconds.
I have determined that this "browser locking" behavior is due mostly to a line of the form:
selection.attr('d', linefn);
...where selection contains around 10K items.
I would like to replace this line with something like
function process_rest () {
if (selection.size() > 0) {
var next_item = first(selection); // function first() is hypothetical!
next_item.attr('d', linefn);
selection = rest(selection); // function rest() is hypothetical!
setTimeout(process_rest, 100);
return;
}
finish_up();
}
setTimeout(process_rest, 100);
I'm looking for an efficient way to implement either first and rest. My very naive guess would be something like:
function first(selection) {
return d3.select(selection[0][0]);
}
function rest(selection) {
selection[0] = selection[0].slice(1);
return selection;
}
...but, AFAIK, this is going "behind the API", or at least feels like it. Is there an "official" (i.e. documented) way to achieve the same result?
EDIT: deleted the shift variant (it's safer not to update selection until after the processing of the first element has been successfully completed).
You can simply use .each():
selection.each(function(d, i) {
setTimeout(function() { d3.select(this).attr("d", linefn); }, i * 100);
});

SlickGrid CollapseAllGroups

I am trying to get dataView.collapseAllGroups() to work with SlickGrid.
The post In SlickGrid, how do I collapse grouping via javascript says to just use collapseAllGroups() but it doesn't seem to work.
Even when going to the current demo page http://mleibman.github.io/SlickGrid/examples/example5-collapsing.html and typing dataView.collapseAllGroups() into the console, it doesn't seem to do anything. Is there something else that I need to do to refresh the grid?
Edit
I was trying to get the Grid to display a tree where the groups are collapsed by default. Although I cannot get CollapseAllGroups() to work, I was able to do a hack by adding "if (item._collapsed == null) item._collapsed = true;" to myFilter function that is in the example above.
This is a rough worksound but it does the job for now until I find the real solution:
function myFilter(item) {
// Added this line:
if (item._collapsed == null) item._collapsed = true;
if (item.parent != null) {
var parent = gridData[item.parent];
while (parent) {
if (parent._collapsed) {
return false;
}
parent = gridData[parent.parent];
}
}
return true;
}
That particular example demonstrates how to implement hierarchies using custom formatters and a filter. It does NOT use the DataView's grouping feature, so the collapseAllGroups() call has no effect.

Calling multiple views in CouchApp query

I need to search the CouchDB based on several criteria entered in a form. Name, an array of Tags and so on. I would then need various views to index on these fields. Ultimately, all the results will be collated in data.js and provided to mustache.html. Say there are 3 views - docsByName, docsByTags, docsById.
What I don't know is, how to query all these views in query.js. Can this be done and how ?
Or should the approach be of that to write one view that makes multiple emits for each search somehow ?
Thank you.
From what you say I assume you are using Evently, so I will quote from Evently primer:
The async function is the main star, which in this case makes an Ajax request (but it can do anything it wants). Another important thing to note is that the first argument to the async function is a callback which you use to tell Evently when you are done with your asynchronous action. [...] Whatever you pass to the callback function then becomes the first item passed to the data function.
In short: put your Ajax requests in async.js.
As a side note: Evently is only one of the possible choices to write a couchapp and it is not clear if it is maintained. However it works and it is easy to rearrange the code to not use it.
EDIT: here is a sample async function (cut&paste from an old program):
function(cb, e) {
var app = $$(this).app
;
app.db.openDoc('SOMEDOCID', {
error: function(code, error, reason) {
alert("Error("+code+" "+error+"): "+reason);
}
, success: function(doc) {
app.view('SOMEVIEWNAME', {
include_docs: true
, error: function(code, error, reason) {
alert("Error("+code+" "+error+"): "+reason);
}
, success: function(resp) {
resp.doc = doc;
cb(resp);
}
});
}
});
}

Resources