Maccurt
Maccurt

Reputation: 13817

Can you test if an element is scrolled to

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

});

Upvotes: 1

Views: 4742

Answers (3)

Rory
Rory

Reputation: 2506

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()

Upvotes: 1

a_halb
a_halb

Reputation: 17

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

cy.get('#SomeId').scrollIntoView()

Upvotes: -1

Josef Biehler
Josef Biehler

Reputation: 1031

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.

Upvotes: 5

Related Questions