-3

I wrote a function that extracts the text of a web element.

getText(selector) { 
        let textValue;
        cy.get(selector).then(($value) => {
            textValue = $value.text()          // working
            return textValue;                 // not returning anything
        })
    }

How can I make getText(selector) return textValue ?

Helper.js class

class Helper {
       getText = (selector) => {
        return cy.get(selector).invoke('text')
            .then(text => {
                return text.trim()   // added to show a .then() step inside function
            })
    }
}

StepDefs.js

        Given("This is demo test", async function (string, string2) {
            const helper = new Helper()
            cy.visit("https://google.com");
                 
            // get Images link text
            let imageText;
            imageText = helpers.getText(':nth-child(2) > .gb_q').then(trimmedText => {
                expect(trimmedText).to.eql('Images'); // this assertion is passing
                return trimmedText 
            })

            //  imageText is not storing a string "Images", but an Object
            cy.task('log', imageText) // it prints [Object object]
    }
paul
  • 4,333
  • 16
  • 71
  • 144
  • 2
    You can't. `cy.get` is asynchronous, you're just adding tasks to the queue not actually getting the value immediately. Read https://docs.cypress.io/guides/core-concepts/variables-and-aliases. – jonrsharpe Mar 10 '23 at 16:34
  • @evolutionxbox no, too complex to understand for a beginner. I have mostly used java selenium – paul Mar 10 '23 at 18:01

2 Answers2

3

As mentioned, you can't directly assign from a cy.get(), you always need a .then() to extract the value.

The question might be, what is the best way?

Using a Cypress.env() is clunky and not necessary, you can just return the the cy.get().then() chain and use the returned parameter.

const getText = (selector) => {
  return cy.get(selector).invoke('text')
    .then(text => {
      return text.trim()   // added to show a .then() step inside function
    })
}

...

getText(selector).then(trimmedText => {
  expect(trimmedText).to.eq('abc');       
})

It works the same as it would if there was no function wrapping the command chain, i.e equivalent to"

cy.get(selector).invoke('text')
  .then(text => { return text.trim() })
  .then(trimmedText => {
    expect(trimmedText).to.eq('abc');       
  })

This is a good way to figure out how to extract certain commands into a function - the function call should be logically equivalent to the commands inside the function.


Reproducible example using Helper class

class Helper {
  getText = (selector) => {
    return cy.get(selector).invoke('text')
     .then(text => {
       return text.trim()  
     })
  }
}

it('tests with Helper class', () => {
  
  cy.visit('http://example.com');
  
  const helper = new Helper()

  helper.getText('h1').then(trimmedText => {
    expect(trimmedText).to.eq('Example Domain')    // passes
  })

})
  • The return type is `[object Object]` not string. I have a `Helper.js` class where I wrote this method and then I am calling `helper.getText()` function in step definitions. I tried `imageText = helper.getText(':nth-child(2) > .gb_q').then(trimmedText => { expect(trimmedText).to.eql('Hello World'); return trimmedText })` – paul Mar 10 '23 at 20:03
  • Helper.js class `getText(selector) { return cy.get(selector).invoke('text') .then(text => { return text.trim() }) .then(trimmedText => { // expect(trimmedText).to.eql('abc'); this.sendLogs("Out getTextModAno for for the selector \"" + selector + "\"" + " is ") }) }` – paul Mar 10 '23 at 20:03
  • ***The return type is not string*** - Correct, as mentioned in comments and at the top of my answer, you cannot get a string returned. You must use a `.then()` to extract the string from the object. – Rab.Scarabelli Mar 10 '23 at 20:14
  • Even after using `.then()` it is returning [Object object]. I have added what I tried in my question. Please check. – paul Mar 10 '23 at 20:46
  • 1
    It works ok for me - have you mixed up `Helper` and `Helpers` ? Both are shown in your code. – Rab.Scarabelli Mar 10 '23 at 21:21
  • No maybe a typo. I didn't – paul Mar 10 '23 at 21:23
  • It's `eql` should be `eq` for comparing text. With that change, the code you posted works. – Rab.Scarabelli Mar 10 '23 at 21:35
  • Can you share your code via gitHub or something? Because it is not working for me :( – paul Mar 10 '23 at 21:39
  • I will add it to the answer... – Rab.Scarabelli Mar 10 '23 at 21:45
  • Hey @Rab, that was not the expectation. Issue was storing the return value in variable `imageText` and using it further (may be for printing in console or other uses). So can you please post a solution where we can store returned string in global or class level variable? – paul Mar 10 '23 at 21:55
  • 1
    Yeah, I think you are flogging a dead horse - it's been said multiple times that you can't do that. There's also a pile of questions asking the same thing, you can take the time to look at those answers to improve understanding. – Rab.Scarabelli Mar 10 '23 at 22:02
  • Do you mean that we can't save an extracted value in a variable to reuse it further, in other functions? What a lame automation framework that would be. – paul Mar 12 '23 at 08:14
-1

Your return only returns the value within the cy.then() command. Your function getText() does not return anything itself.

const getText = (selector) => {
  return cy.get(selector).invoke('text');
}

However, that would return a variable of type Chainable<String>, and not simply String. You could use this in a separate Cypress chain to validate the text like so:

getText('foo').should('eq', 'bar');

Or, if you needed to access the variable outside of a Cypress chain, you could re-write the getText() function to store the value in a Cypress environment variable.

const getText = (selector) => {
  return cy.get(selector).invoke('text').then((text) => {
    Cypress.env('textVal', text);
  });
}
...
getText('foo').then(() => {
  expect(Cypress.env('textVal')).to.eql('foo');
});

If storing the value in a Cypress environment variable, you'll want to make sure that you're handling the asynchronous nature of Cypress commands in some way, which I've done in the above example by only calling Cypress.env() once I'm within a cy.then() function

agoff
  • 5,818
  • 1
  • 7
  • 20