How to return dimensions of document in Cypress fo

2020-07-22 18:19发布

I have a function in Cypress support/index.js that is meant to get the dimensions of the cy.document outerWidth and outerHeight, then return them for future use in a test. My problem is that when the test runs and the values are compared with others the assertion says the values are NaN. I checked by console logging the value at the point of the assertion and it was empty, so I must be doing something wrong, I'm just not sure what. My function is below, any help gratefully received, thanks.

function getViewport() {
  var viewport = {}
  cy.document().then((doc) => {
    let width = Cypress.$(doc).outerWidth()
    let height = Cypress.$(doc).outerHeight()
    viewport['bottom'] = height
    viewport['height'] = height
    viewport['left'] = 0
    viewport['right'] = width
    viewport['top'] = 0
    viewport['width'] = width
    viewport['x'] = 0
    viewport['y'] = 0
  }).then(() => {
    return viewport
  })
  return viewport
}

The code that calls getViewport() is

export const getRect = (obj) => {
  var rect
  if (obj == 'viewport') {
    rect = getViewport()
  } else {
    rect = getElement(obj)
    if (Cypress.config('parseLayoutToInt')) { rect = parseAllToInt(rect) }
  }
  return rect
}

And that is called by a custom command, where subject is prevSubject and the element is the string "viewport"

Cypress.Commands.add('isInside', { prevSubject: true }, (subject, element, expected) => {
  var minuend, subtrahend, diff
  minuend = getRect(element)
  subtrahend = getRect(subject)
  diff = getRectDiff(minuend, subtrahend, expected);
  expect(diff).to.deep.equal(expected);
})

标签: cypress
3条回答
相关推荐>>
2楼-- · 2020-07-22 19:07

I ran into this problem as well, and opted for a solution with async/await:

function getDocument() {
  return new Promise(resolve => {
    cy.document().then(d => {
      console.log('deeee', d);
      resolve(d);
    });
  });
}

describe('Stuff', () => {
  it('Sees the toasty character', async () => {
    const document = await getDocument();

    // Your test code here
  });
});

Even though Cypress commands aren't really promises, you can create your own promise, and resolve it when ready. Then await that promise in your test code.

Hope it helps!

查看更多
相关推荐>>
3楼-- · 2020-07-22 19:13

Like @NoriSte said, the cy commands are asynchronous thus you can't mix them with sync code.

What you want to do is something like:

function getViewport() {
  return cy.document().then( doc => {
    rect = /* do something synchronous */
    return rect;
  });
}

Anyway, to answer the original question (in the title), there's a couple of patterns I use to store a value for later use in cypress:

  1. wrap next commands in the then callback:

    cy.document().then( doc => {
        return doc.documentElement.getBoundingClientRect();
    }).then( viewportRect => {
    
        cy.doSomething(viewportRect);
        cy.doSomethingElse();
    });
    
  2. cache to a variable and access the cached value from inside an enqueued command:

    let viewportRect;
    
    cy.document().then( doc => {
        return doc.documentElement.getBoundingClientRect();
    }).then( rect => viewportRect = rect );
    
    cy.doSomething();
    
    // this is important -- you need to access the `viewportRect`
    // asynchronously, else it will be undefined at the time of access
    // because it's itself assigned asynchronously in the first command'd callback
    cy.then(() => {
        doSomething(viewportRect);
    });
    

Ad the actual problem in your question (if I understood it correctly), I've made a solution you can learn from:

const getRect = (selector) => {
    if (selector == 'viewport') {
        return cy.document().then( doc => {
            return doc.documentElement.getBoundingClientRect();
        });
    } else if ( typeof selector === 'string' ) {
        return cy.get(selector).then( $elem => {
            return $elem[0].getBoundingClientRect();
        });
    // assume DOM elem
    } else {
        return cy.wrap(selector).then( elem => {
            return Cypress.$(elem)[0].getBoundingClientRect();
        });
    }
};

const isInside = (containerRect, childRect) => {
    if ( !containerRect || !childRect ) return false;
    return (
        childRect.top >= containerRect.top &&
        childRect.bottom <= containerRect.bottom &&
        childRect.left >= containerRect.left &&
        childRect.right <= containerRect.right
    );
};

Cypress.Commands.add('isInside', { prevSubject: true }, (child, container, expected) => {
    return getRect(child).then( childRect => {
        getRect(container).then( containerRect => {
            expect(isInside(containerRect, childRect)).to.equal(expected);
        });
    });
});

describe('test', () => {
    it('test', () => {
        cy.document().then( doc => {
            doc.body.innerHTML = `
                <div class="one"></div>
                <div class="two"></div>
                <style>
                    .one, .two {
                        position: absolute;
                    }
                    .one {
                        background: rgba(255,0,0,0.3);
                        width: 400px;
                        height: 400px;
                    }
                    .two {
                        background: rgba(0,0,255,0.3);
                        width: 200px;
                        height: 200px;
                    }
                </style>
            `;
        });
        cy.get('.two').isInside('.one', true);
        cy.get('.one').isInside('.two', false);
    });

    it('test2', () => {
        cy.document().then( doc => {
            doc.body.innerHTML = `
                <div class="one"></div>
                <div class="two"></div>
                <style>
                    body, html { margin: 0; padding: 0 }
                    .one, .two {
                        position: absolute;
                    }
                    .one {
                        background: rgba(255,0,0,0.3);
                        width: 400px;
                        height: 400px;
                    }
                    .two {
                        background: rgba(0,0,255,0.3);
                        width: 200px;
                        height: 200px;
                        left: 300px;
                    }
                </style>
            `;
        });
        cy.get('.two').isInside('.one', false);
        cy.get('.one').isInside('.two', false);
    });
    it('test3', () => {
        cy.document().then( doc => {
            doc.body.innerHTML = `
                <div class="one"></div>
                <style>
                    body, html { margin: 0; padding: 0 }
                    .one {
                        position: absolute;
                        background: rgba(255,0,0,0.3);
                        width: 400px;
                        height: 400px;
                        left: -100px;
                    }
                </style>
            `;
        });
        cy.get('.one').isInside('viewport', false);
    });
});
查看更多
Lonely孤独者°
4楼-- · 2020-07-22 19:21

Why there is a synchronous return in your getViewport function? I'm speaking about the last return viewport

function getViewport() {
  var viewport = {}
  cy.document().then((doc) => {
    ...
  })

  return viewport // <-- ?????
}

doing so, all the cy.document().then((doc) etc. code is useless.

I don't know if this is the problem, but I can't run your code locally because it misses a lot of functions. Could you share a "working” GitHub repo to make some more tests?

查看更多
登录 后发表回答