Can you test if an element is scrolled to - cypress

I have a link that when clicked it will scroll to div element on the same page. Is there a way I can click on a link and determine that the element was actually scrolled to?
describe('scrolling.spec.js', () => {
it('should scroll to an element.', () => {
cy.visit('/home');
cy.get('#calculator-link').click();
//verify that the element with id payment-calculator is scrolled to
///??
});
});

Yes, you can do this.
When working with Cypress you always can ask you "how would I do this in plain JS (or with JQuery)" and in most cases, you can apply exactly the same codé in cypress.
In plain JS you probably would get getClientRect() to retrive the top of the element within it's parent. So do exactly the same in Cypress.
Also you can use get(...).should("be.visible") because an element is not visible if it is not in the view. This applies if your element have a parent container that has set a max height and a overflow: scroll (only an example, there will be more situation where this is working).
So please look at the code:
describe("scroll", () => {
beforeEach(() => {
cy.visit("scroll.html")
})
describe("by using is.visible", () => {
it("is.visible fails if not in view", () => {
cy.get("#toscroll").should("be.visible");
})
it("is.visible succeeds if element in view", () => {
cy.get("#toscroll").then($e => $e[0].scrollIntoView());
cy.get("#toscroll").should("be.visible");
})
})
describe("by using clientRect", () => {
it("fails without scrolling", () => {
cy.get("#toscroll").should($e => {
expect($e[0].getClientRects()[0].top).lessThan(100);
});
})
it("clientRect should have correct size", () => {
cy.get("#toscroll").then($e => $e[0].scrollIntoView());
cy.get("#toscroll").should($e => {
expect($e[0].getClientRects()[0].top).lessThan(100);
});
})
})
})
It shows both possibilities. My html looks like this:
<html>
<head>
<style>
#container {
height: 300px;
overflow: scroll;
}
</style>
</head>
<body>
<div id="container">
<div>---</div>
<div>---</div>
..... much more elements
<div id="toscroll">is visible</div>
<div>---</div>
..... much more elements
</div>
</body>
</html>
Explanation about the usage of "plain" JS:
Cypress provides you many assertions that can be chained off CY commands. E.g. should("exist"). But you also can pass a callback into should and this callback will be repeated until the timeout has been reached or no assertion fails.
Within this callback you can access the JQuery element that was yielded from the previous command. At this moment you can do whatever you want with this object.

I use the following custom command to test if the top of an element is visible in the area that has currently been scrolled to. It fails if the element is completely off the top or the bottom of the visible area.
/cypress/support.index.js
Cypress.Commands.add("isScrolledTo", { prevSubject: true }, (element) => {
cy.get(element).should(($el) => {
const bottom = Cypress.$(cy.state("window")).height();
const rect = $el[0].getBoundingClientRect();
expect(rect.top).not.to.be.greaterThan(bottom, `Expected element not to be below the visible scrolled area`);
expect(rect.top).to.be.greaterThan(0 - rect.height, `Expected element not to be above the visible scrolled area`)
});
});
In tests:
cy.get('#payment-calculator').isScrolledTo()

According to the docs, one could simply use scrollIntoView() chained behind cy.get() like this:
cy.get('#SomeId').scrollIntoView()

Related

Draggable not working with AJAX request - Angular

I would like to implement elements with a draggable property. You get what you wanted in part. I have several blocks that are built from the response of a web-service.
Use ng-repeat to represent the elements on the screen:
<div class = "declareContainer" ng-repeat = "item in group">
<!-- My Draggable Boxes -->
<div id="{{$index}}" title="{{$index}}" class="col-lg-6" style=" border radius: 15px; padding: 1%; ">
</div>
The loop is controlled by "group" object, however it is obtained by AJAX:
$Http.get (path + "getgroup"). Then (function (response) {
$ Scope.group = response.data;
}, Function (error) {
});
I use the following excerpt in my controller to apply a draggable property to the elements that are in the declareContainer div:
Angular.element (document) .ready (function () {
$ (".declareContainer"). Draggable ();
});
Elements are drawn normally, but lose a draggable property.
I ran tests and noticed that when defining the group object in static or exploit code (ng-repat already gets as object of the object during the construction of the app).
I tried to initialize "group" as null for the loop not toenter code here run while a response is not, but still giving the same problem.
Does anyone know of any way to solve this problem?

Getting the count of elements in a list from Nightwatch

I have the following DOM structure:
<div data-qa-id="criteriaDisplay">
<ul>
<li>...</li>
<li>...</li>
<li>...</li>
</ul>
</div>
I would like to write a command that returns the number of items in the list above. I am a newbie to Nightwatch. Here's my attempt to do this. My thinking is to start with the <ul> under the criteriaDisplay element and then count the <li> items under it. So I am trying to use the elementIdElements api, but it is not working:
var criteriaDisplayCommands = {
getNumberOfItems: function(callback) {
var self = this;
console.log(this);
return this.api.elementIdElements('#myList', 'css selector', 'li', function(result) {
callback.call(self, result);
});
}
};
module.exports = {
url: function() {
return this.api.launchUrl + '/search';
},
sections: {
criteriaDisplay: {
selector: '[data-qa-id="criteriaDisplay"]',
commands: [criteriaDisplayCommands],
elements: {
myList: 'ul'
}
}
}
};
Can someone please help me with the right way to do this?
I dont think this is right:
return this.api.elementIdElements('#myList', 'css selector', 'li', function(result) {
callback.call(self, result);
});
Since elementIdElements requires the first parameter to be an elementId. In your case its a css selector.
The second i'm not sure about is that you are using custom selectors in the selenium api. I dont think that this is gonna work (but i'm not sure about this)
From the doc's section about defining-elements:
Using the elements property allows you to refer to the element by its name with an "#" prefix, rather than selector, when calling element commands and assertions (click, etc).
Since you are using the selenium api and not a command or assertion, i dont think that it would be a valid input.
You could use elements for getting your result, or an combination of element and elementIdElements.
The simple one is only using elements as shown below:
this.api.elements('css selector', 'ul li', function (result) {
console.log(result.value.length) //(if there are 3 li, here should be a count of 3)
callback.call(self, result);
});`

Qtip2- tooltip with hidden element not shown

I´m using the http://qtip2.com/ tooltips. I want to use a hidden element, for that i´m using this code:
<script type="text/javascript">
// <![CDATA[
// Grab all elements with the class "hasTooltip"
$('.hasTooltip').each(function() { // Notice the .each() loop, discussed below
$(this).qtip({
content: {
text: $(this).next('div') // Use the "div" element next to this for the content
}
});
});
// ]]>
And under this code:
<div class="hasTooltip">Hover me to see a tooltip</div>
<div class="hidden">
<!-- This class should hide the element, change it if needed -->
<p><strong>Complex HTML</strong> for your tooltip <em>here</em>!</p>
</div>
If you hover over the "Hover me to see a tooltip", no tooltip is shown? No idea, why?
events: {
render: (event, api) ->
show: (event, api) ->
$('.qtip:visible').qtip('hide')
elements = $(api.get('content.text')).removeClass('hidden')
api.set('content.text', elements)
,
hide: (event, api) ->
}
I think its a bug in Qtip2, i had to manually remove the class

kendo ui grid within a window - window becomes invisible

I'm using Kendo UI 2013.1 and I have a grid within a window. The window's visibility is set to false on the page load, but when a link is clicked, I make it visible.
The problem is that whenever you try to do anything with the grid, like use the filter, or use a paging button, the window becomes invisible. When you click the link again, the window is visible again and reflects whatever the last action was - filtered results or on the next page.
I've tried several approaches that look similar to:
$("#outageWindow").kendoWindow({ visible: true });
But no luck. Here is the full code without any of my resolution attempts:
#(Html.Kendo().Window()
.Name("viewListWindow")
.Title("Complete CI List")
.Width(650)
.Actions(actions => actions.Close())
.Content(#<text>
#(Html.Kendo().Grid(chg.CIsModifiedByChange.CIsModifiedByChange) //Bind the grid to ViewBag.Products
.Name("grid")
.RowAction(row =>
{
if (row.IsAlternate)
{
//Set the background of the entire row
//row.HtmlAttributes["style"] = "background:#e0f7ff;"; this is a lighter blue
row.HtmlAttributes["style"] = "background:#dde1ff;";
}
})
.Columns(columns =>
{
columns.Bound(ci => ci.Value).Title("CI Name");
})
.Pageable() // Enable paging
.Sortable() // Enable sorting
.Filterable() // Enable filtering
)
</text>)
.Draggable()
.Visible(false)
)
<script type="text/javascript">
$(document).ready(function () {
$("#viewCI").bind("click", function () {
$("#viewListWindow").data("kendoWindow").center().open();
})
});
</script>
this solution is work fine for me,
try this
function load_grid() {
/* your grid properties here */
}
$(document).ready(function () {
$("#viewCI").bind("click", function () {
/* load window */
$("#viewListWindow").data("kendoWindow").center().open();
/* load grid into element inside window after window opened */
load_grid();
})
});

Kendo splitter control load right panel contents asynchronously

I have a Kendo splitter control with left/right panes. Inside the left pane I have a Kendo panel bar control that builds a navigation menu. Unfortunately I inherited this from another developer that left the company and I'm not familiar with the Kendo control.
It all works, but when the user clicks on a menu item, the entire page refreshes, That's for the birds! I want only the right panel to refresh.
Here's the code for the for the layout page:
<body>
#(Html.Kendo().Splitter().Name("splitter").Panes(panes => {
panes.Add().Size("220px").Collapsible(true).Content(#<text>
#Html.Partial("_Panelbar")
</text>);
panes.Add().Content(#<text>
<section id="main">
#RenderBody()
</section>
</text>);
}))
<script type="text/javascript">
$(document).ready(function () {
$('input[type=text]').addClass('k-textbox');
});
</script>
#RenderSection("scripts", required: false)
</body>
and here's the code for the panel partial view:
#(Html.Kendo().PanelBar().Name("panelbar")
.SelectedIndex(0)
.Items(items => {
items.Add().Text("Corporate").Items(corp =>
{
corp.Add().Text("Vendors").Action("Index", "Vendor");
corp.Add().Text("Materials").Action("Index", "CostMaterials");
corp.Add().Text("Packaging").Action("Index", "CostPackaging");
corp.Add().Text("Reports").Action("UnderConstruction", "Home", new { pageTitle = "Reports" });
});
}))
I tried replacing the .Action method on the PanelBar with LoadContentsFrom method. That replaced the content in the left pane. So I guess my question is, how do I target the right side of the splitter control?
Any help would be appreciated.
Thanks
-Alex
Your code maybe like this:
#(Html.Kendo().PanelBar().Name("panelbar")
.SelectedIndex(0)
.Items(items => {
items.Add().Text("Corporate").Items(corp =>
{
corp.Add().Text("Vendors").Url("javascript:void(0);")
.HtmlAttributes(
new {
#class= "helloWorld",
#data-href="/Vendor/Index"
});
});
}))
<script>
$document.ready(function(){
$('.helloWorld').click(function(){
var href = $(this).attr('data-href');
$('#main').load(href);
});
});
</script
UPDATE
There is one thing very important: I think the view /Vendor/Index have the same template with your current page.
It means that when you load /Vendor/Index into the right side. The right side will include entire content (include left panel again).
Solution
You have to create a new view(a template) , which just include your left menu,banner,...
Then, You have to remove all template of other views (which will be loaded into right side - /Vendor/Index , /CostMaterials/Index,...)
2.This way is not a good approach. But I think It will work.
//Reference : Use Jquery Selectors on $.AJAX loaded HTML?
<script>
$document.ready(function(){
$('.helloWorld').click(function(){
var href = $(this).attr('data-href');
$.ajax({
type: 'GET',
url: href,
success: function (data){
var rHtml = $('<html />').html(data);
$('#main').html(rHtml.find('#main'));
}
});
});
});
</script

Resources