Solving Scope and Visibility Issues with { withinSubject}

Automating SAP Hybris Promotions with Cypress: Overcoming Scrolling and Visibility Challenges

Cypress is an exceptional tool for end-to-end testing, providing a robust environment for automating complex interactions within web applications. One such challenging scenario involves automating tests within SAP Hybris, mainly when dealing with a list of promotions presented in a table format. This article delves into a specific issue encountered during this process and explores the solution, providing a comprehensive guide for others facing similar challenges.

The Scenario

Imagine you are tasked with automating the process of finding and clicking a specific promotion in SAP Hybris. The promotions are listed in a table, and each promotion is uniquely identified by four values:
promotionCode,
promotionName,
websiteEnv,
and promotionStatus.
However, multiple variations of the same promotion exist, with identical promotionCode, promotionName, and websiteEnv values but differing promotionStatus values. The unique identifier, in this case, is the Published status.

Here’s the problem: the promotionStatus value is not initially visible in the viewport, making it challenging for Cypress to interact with it directly.

The Initial Approach

The initial code aimed to locate the promotion by iterating through each row in the table and checking if all four criteria matched. If found, it would click on the row. The code snippet used was:

cy.get('tr.yw-coll-browser-hyperlink').each(($row) => {
    cy.wrap($row).within(() => {
        const promotionCode = 'CR_PMI_MGM_Godfather_Discount_01_More';
        const promotionName = 'MGM GODFATHER Reward';
        const websiteEnv = 'Costa Rica_Promotion_Group';
        const promotionStatus = 'Published';

        cy.get('.yw-listview-cell-label.z-label').contains(promotionCode).should('exist')
            .then(() => {
                cy.get('.yw-listview-cell-label.z-label').contains(promotionName).should('exist')
                    .then(() => {
                        cy.get('.yw-listview-cell-label.z-label').contains(websiteEnv).should('exist')
                            .then(() => {
                                cy.get('div.z-listbox-body').should('exist').scrollTo('right', { ensureScrollable: true }).wait(500);
                                cy.get('.yw-listview-cell-label.z-label')
                                    .contains(promotionStatus).should('be.visible')
                                    .then(($status) => {
                                        cy.log('Found status: ', $status.text());
                                        cy.wrap($row).click({ force: true });
                                    });
                            });
                    });
            });
    });
});

The Problem

The code successfully found the promotion code, name, and environment values but failed to locate the Published status. This happened because the Published status was not in the viewport and Cypress could not scroll the table horizontally as intended. This limitation caused Cypress to throw an error: expected div. list box.z-flex-item to exist in the DOM.

The Solution

The core of the issue lies in the incorrect scope of the search for the z-listbox-body element. The cy.get('div.z-listbox-body') command was inside the cy.wrap($row).within(() => {}) block, restricting the search scope to the row, whereas z-listbox-body was outside this row.

To resolve this, we need to specify the withinSubject option to expand the search scope. The corrected approach is:

cy.get('tr.yw-coll-browser-hyperlink').each(($row) => {
    cy.wrap($row).within(() => {
        const promotionCode = 'CR_PMI_MGM_Godfather_Discount_01_More';
        const promotionName = 'MGM GODFATHER Reward';
        const websiteEnv = 'Costa Rica_Promotion_Group';
        const promotionStatus = 'Published';

        cy.get('.yw-listview-cell-label.z-label').contains(promotionCode).should('exist')
            .then(() => {
                cy.get('.yw-listview-cell-label.z-label').contains(promotionName).should('exist')
                    .then(() => {
                        cy.get('.yw-listview-cell-label.z-label').contains(websiteEnv).should('exist')
                            .then(() => {
                                cy.get('div.z-listbox-body', { withinSubject: Cypress.$('body')[0] }).scrollTo('right', { ensureScrollable: true }).wait(500);
                                cy.get('.yw-listview-cell-label.z-label')
                                    .contains(promotionStatus).should('be.visible')
                                    .then(($status) => {
                                        cy.log('Found status: ', $status.text());
                                        cy.wrap($row).click({ force: true });
                                    });
                            });
                    });
            });
    });
});

Beside this command:
{ withinSubject: Cypress.$('body')[0] }
I’ve also tried using the next one and it worked as well:
{withinSubject:null}

Key Insights and Best Practices

  1. Understanding Scope with withinSubject: The withinSubject option is crucial when you need to search outside the initially scoped element. This approach allows Cypress to locate elements beyond the immediate context, enabling more flexible interactions.
  2. Scrolling and Visibility: While Cypress can interact with elements outside the viewport using .scrollIntoView() or .click({ force: true }), understanding when and how to use these commands effectively can simplify your test scripts and improve reliability.
  3. Debugging Tips: When Cypress cannot find an element, verify the element’s presence in the DOM and check the scope of your search commands. Using .should('exist') is a good practice to ensure the element is loaded before interacting with it.
Scroll to Top