mmonfared / CyFramework

A test automation framework using Cypress
40 stars 9 forks source link

How to use cy.origin() in step_definitions #2

Closed rohShinde closed 1 year ago

rohShinde commented 1 year ago

First I want to thank you for describing things to help setup the project.

I am facing one issue in automating my application using your way of cypress + BDD. The issue is in step definitions where my application login process deals with two different urls.

Given('I am on the landing page', () => {
     cy.visit('first_url')
     cy.origin('second_url', () => cy.contains('Log in to Page'))
})

using cy.origin() helps me change the active url application currently have, however while trying POM I am getting error as Variables must either be defined within the cy.origin() command or passed in using the args option.

mmonfared commented 1 year ago

First I want to thank you for describing things to help setup the project.

I am facing one issue in automating my application using your way of cypress + BDD. The issue is in step definitions where my application login process deals with two different urls.

Given('I am on the landing page', () => { cy.visit('first_url') cy.origin('second_url', () => cy.contains('Log in to Page')) })

using cy.origin() helps me change the active url application currently have, however while trying POM I am getting error as Variables must either be defined within the cy.origin() command or passed in using the args option.

Hi @rohShinde, you're welcome! My pleasure. I think this is not related to step definitions. In cy.origin(), you should serialize the variables and data you want to use in call back function. This should be done with args option which is an object sent from the primary origin to the secondary origin, where it will be deserialized and passed into the callback function as its first and only argument. This is the only available way without violating the browser's same-origin policy. Because cypress runs directly inside browser.

Look at this example, assume you want to signup in application and you should click on Complete registration button or GET request its href attribute (containing token) from your email which is in another origin (Ethereal.email). I'm serializing identity variable and etheralMailLocators object with args. Maybe this is your problem's root cause.

import { signupLocators, etheralMailLocators, sharedLocators } from "./locators"
import { generateRandomIdentity } from "../support/utils"

it('Should signup succesfully', () => {
        cy.visit('/sign-up')
        let identity = generateRandomIdentity()
        cy.get('button').contains('Agree').click()
        cy.get(signupLocators.EMAIL_INPUT).type(identity.email)
        cy.get(signupLocators.PASSWORD_INPUT).type(identity.password)
        cy.get(signupLocators.PASSWORD_CONFIRM_INPUT).type(identity.password)
        cy.get(signupLocators.ACCEPT_TERMS_CHECKBOX).check()
        cy.get(signupLocators.CREATE_ACCOUNT_BUTTON).should('be.enabled').click()
        cy.origin('https://ethereal.email', { args: { etheralMailLocators, identity } }, ({ etheralMailLocators, identity }) => {
            cy.get(etheralMailLocators.EMAIL_ADDRESSES).eq(1).should('have.attr', 'title', identity.email)
            cy.get("iframe").then(($iframe) => {
                const iFrameContents = $iframe.contents().find('body')
                cy.wrap(iFrameContents).contains('Welcome to AutomationCamp')
                cy.wrap(iFrameContents).find(etheralMailLocators.COMPLETE_REGISTRATION_BUTTON).click()
            })
        })
        cy.get(sharedLocators.GENERIC_NOTIFICATION).should('be.visible').contains("Your email has been successfully associated with your account")
        cy.location('pathname').should('eq', '/dashboard')
        cy.get(sharedLocators.PROFILE_BUTTON).should('be.visible')
rohShinde commented 1 year ago

@mmonfared Thanks for your help on this I am now able to use cy.origin() with steps. I am now writing my BDD steps as

Given('I am on the landing page', () => {
    cy.visit('/')
    cy.origin(loginPageUrl, () => cy.contains('Log in to Application'))
})

When('I click on the login with email button', () => { 
    cy.origin(loginPageUrl, () => {
        cy.get('div:nth-child(1) > a > button').click()
    })
})

When('I type in my email address {string}', (value) => {
    cy.origin(loginPageUrl, {args: {value}}, ({ value }) => {
        cy.get('#login').type(value)
        cy.wait(500)
    })
})

However still not able figure out a way to use POM style syntax with this, if I wrote something like

const loginPage = new LoginPage()

When('I click on the login with email button', () => { 
    cy.origin(loginPageUrl, {args: {loginPage}}, ({ loginPage }) => {
        loginPage.clickLoginWithEmail.click()
    })
})

Its giving me error as loginPage is not defined.

mmonfared commented 1 year ago

@rohShinde I think it is not possible to pass the class instance as an object to cy.origin.

Can you try this:

const loginPage = new LoginPage()

When('I click on the login with email button', () => { 
    cy.origin(loginPageUrl, () => {
        loginPage.clickLoginWithEmail.click()
    })
})

Or this:

When('I click on the login with email button', () => { 
    cy.origin(loginPageUrl, () => {
        const loginPage = new LoginPage()
        loginPage.clickLoginWithEmail.click()
    })
})
rohShinde commented 1 year ago

Nevermind, we can close this issue, I m following the classic way than POM for login scenario and for all other places using POM. Though you suggested second option seems to work but it will increase the duplicacy in code.