0

I'm trying to overwrite the Cypress within method such that there's an extra argument for the callback.

Ultimately, my goal is to be able to go cy.customCommandThatSetsAValue().within((subject, extra) => {})

The issue seems to be a matter of timing... whenever I try to pass a non-hardcoded value, it ends up being passed as undefined. I've tried a number of permutations and it seems like somehow even when I set the variable from within a .then() callback, it's undefined when it gets to the custom command.

Here's my override:

function within<Subject>(
    originalWithinFn: Cypress.CommandOriginalFnWithSubject<'within', Subject>,
    prevSubject: Subject, 
    optionsOrFn: Partial<Cypress.Loggable> | WithinCallback,
    fnOrUndefined?: WithinCallback,
){
    let fn: WithinCallback;
    let options: Partial<Cypress.Loggable> | undefined;
    if (optionsOrFn instanceof Function && fnOrUndefined === undefined) {
        fn = optionsOrFn;
    } else if (fnOrUndefined instanceof Function) {
        options = optionsOrFn;
        fn = fnOrUndefined;
    } else {
        throw new Error('Invalid arguments provided to cy.within');
    }
    return getMostRecentNavigationShorthand().then(mostRecentShorthand => originalWithinFn(prevSubject, options!, (subject) => fn(subject, mostRecentShorthand)));
}

Cypress.Commands.overwrite('within', within);

A simplified version of customCommandThatSetsAValue() is

return cy.get('foo').then(() => {mostRecentShorthand = anObject});

and

function getMostRecentNavigationShorthand() {
    return new Cypress.Promise((resolve, reject) => resolve(mostRecentShorthand));
}

I've tried changing the return in within to

return cy.then(() => 
    getMostRecentNavigationShorthand()
        .then(mostRecentShorthand => 
            originalWithinFn(prevSubject, options!, (subject) => fn(subject, mostRecentShorthand)
)));

as well as trying to wrap a cy.then around the promise (or use that instead of a promise). No matter what i do, it still ends up undefined at actual execution time.

Any idea how/why I keep getting undefined or how to fix it? Thanks!

user3534080
  • 1,201
  • 1
  • 14
  • 21

1 Answers1

3

If I put all that code into a test, it passes. Don't know what anObject is, so I just used {abc: 123}.

let mostRecentShorthand = null

function getMostRecentNavigationShorthand() {
  return new Cypress.Promise((resolve, reject) => resolve(mostRecentShorthand));
}

function within(originalWithinFn, prevSubject, optionsOrFn, fnOrUndefined) {

  // parameter resolving

  return getMostRecentNavigationShorthand()
    .then(mostRecentShorthand => {
      return originalWithinFn(prevSubject, options, (subject) => 
        fn(subject, mostRecentShorthand)
      )
    })
}
Cypress.Commands.overwrite('within', within)

Cypress.Commands.add('customCommandThatSetsAValue', () => {
  return cy.get('h1').then(() => {mostRecentShorthand = {abc: 123}})
})

it('gives an extra parameter to within', () => {
  cy.visit('https://example.com');
  cy.customCommandThatSetsAValue().within((subject, extra) => {
    expect(extra).to.deep.eq({abc: 123})
  })
})

You would not need the Cypress.Promise block since you set the value synchronously, immediately resolve, and never reject. But it does not cause the extra parameter to be undefined. That would only happen if the value of anObject were undefined.

Klea
  • 169
  • 7
  • Very strange... I replaced setting that value by hand with `cy.wrap(theValue).as('___mostRecentShorthand').get('foo')` and suddenly it isn't undefined anymore. But now the command override is complaining about mixing sync and async, even though the only changes over there were replacing `getMostRecentNavigationShorthand` with another `cy.get` chain – user3534080 Jul 14 '23 at 15:14
  • And that error appears to have been caused by returning `originalWithinFn` as opposed to just calling it – user3534080 Jul 14 '23 at 15:20