简体   繁体   English

赛普拉斯中难以访问窗口对象

[英]Difficulty accessing window object in Cypress

I'm trying to access the window object of my App in Cypress in the following manner.我正在尝试以下列方式访问赛普拉斯中我的应用程序的窗口对象。

cy.url().should('include', '/home').then(async () => {
    const window = await cy.window();
    console.log(window);
});

The above method is not working for me as window is returned as undefined .上述方法对我不起作用,因为window返回为undefined

However, the answer in this SO post states the following:但是, 此 SO 帖子中的答案如下:

Or you can use cy.state('window') which returns the window object synchronously, but this is undocumented and may change in the future.或者您可以使用 cy.state('window') 同步返回窗口对象,但这是未记录的,将来可能会更改。

This method does return the window value successfully.此方法确实成功返回了窗口值。

cy.url().should('include', '/home').then(async () => {
    const window = cy.state('window');
    console.log(window);
});

As the answer suggests, cy.state('window') is undocumented so I would still rather use cy.window().正如答案所暗示的那样,cy.state('window') 没有记录,所以我仍然宁愿使用 cy.window()。 Is there any reason why it's returning undefined?是否有任何理由返回未定义? (I started learning cypress today.) (我今天开始学习柏树。)

This comes up frequently.这经常出现。 Cypress has some documentation stating Commands are not Promises .赛普拉斯有一些文档说明Commands are not Promises I did a write up using a custom command to force a Command Chain to behave like a promise, but it is still experimental and nuanced.我写了一篇使用自定义命令来强制命令链表现得像一个承诺的文章,但它仍然是实验性的和细微的。

First I'll give your example almost verbatim to what you're trying to accomplish:首先,我将几乎逐字逐句地给出您要完成的示例:

cy.url().should('include', '/home').then(() => {
  cy.window().then(win => {
    console.log(win) // The window of your app, not `window` which is the Cypress window object
  })
})

Your example could be written a number of ways, but maybe explaining a bit how Cypress works will help more.您的示例可以通过多种方式编写,但也许稍微解释一下赛普拉斯的工作原理会有所帮助。

Cypress has something called "Commands" that return new "Chainers".赛普拉斯有一个叫做“命令”的东西,它返回新的“链子”。 It is fluid syntax like JQuery:它像 JQuery 一样流畅的语法:

// fill and submit form
cy
  .get('#firstname')
  .type('Nicholas')
  .get('#lastname')
  .type('Boll')
  .get('#submit')
  .click()

You can (and should) break up chains to be more like sentences:您可以(并且应该)将链分解为更像句子:

// fill and submit form
cy.get('#firstname').type('Nicholas')
cy.get('#lastname').type('Boll')
cy.get('#submit').click()

You might have guessed that all Cypress Chainer commands are asynchronous.您可能已经猜到所有 Cypress Chainer 命令都是异步的。 They have a .then , but they aren't actually promises.他们有一个.then ,但实际上并不是承诺。 Cypress commands actually enqueue.赛普拉斯命令实际上是排队的。 Cypress hooks into mocha's lifecycle to make sure a before , beforeEach , it , afterEach , after block waits until Cypress commands are no longer enqueued before continuing. Cypress 挂钩 mocha 的生命周期,以确保beforebeforeEachitafterEachafter块等到 Cypress 命令不再排队后再继续。

Let's look at this example:让我们看一下这个例子:

it('should enter the first name', () => {
  cy.get('#firstname').type('Nicholas')
})

What actually happens is Cypress sees the cy.get Command and enqueues the get command with the argument '#firstname' .实际发生的是赛普拉斯看到cy.get命令并使用参数'#firstname'get命令排入队列。 This immediately (synchronously) returns execution to the test.这立即(同步)将执行返回到测试。 Cypress then sees the cy.type command with the argument 'Nicholas' and returns immediately to the test. Cypress 然后看到带有参数'Nicholas'cy.type命令并立即返回测试。 The test is technically done at this point since there is no done callback and no Promise was returned.此时测试在技术上已经完成,因为没有done回调并且没有返回 Promise。 But Cypress hooks into the lifecycle of mocha and doesn't release the test until enqueued commands are completed.但是 Cypress 挂钩到 mocha 的生命周期,并且在队列命令完成之前不会发布测试。

Now that we have 2 enqueued commands and the test is waiting for Cypress to release the test, the get command is popped off the queue.现在我们有 2 个排队的命令并且测试正在等待赛普拉斯释放测试, get命令从队列中弹出。 Cypress will try to find an element on the page with an id of firstname until it finds it or times out. Cypress 将尝试在页面上查找 id 为firstname的元素,直到找到它或超时。 Assuming it finds the element, it will set a state variable called subject ( cy.state('subject') , but don't rely on that).假设它找到了元素,它将设置一个名为subject的状态变量( cy.state('subject') ,但不要依赖它)。 The next enqueued command type will grab the previous subject and attempt to type each key from the string 'Nicholas' one at a time with a default delay of 50ms until the string is done.下一个入队命令type将抓取前一个subject ,并尝试一次键入字符串'Nicholas'中的每个键,默认延迟为 50 毫秒,直到字符串完成。 Now there are no more enqueued commands and Cypress releases the test and the runner continues to the next test.现在没有更多的排队命令,赛普拉斯发布测试,运行程序继续下一个测试。

That was a bit simplified - Cypress does a lot more to make sure .type only runs on elements that can receive focus and are interactable, etc.这有点简化了——赛普拉斯做了很多工作来确保.type只在可以接收焦点和可交互的元素上运行,等等。

Now, knowing this, you can write your example a bit more simply:现在,知道了这一点,您可以更简单地编写示例:

cy.url().should('include', '/home')

// No need for `.then` chaining or async/await. This is an enqueued command
cy.window().then(win => {
  console.log(win)
})

For me, the accepted answer is good but not really showing me what is necessary.对我来说,接受的答案很好,但并没有真正向我展示什么是必要的。

For me, it is awesome that everything is synchronous and you can do things like对我来说,一切都是同步的,你可以做这样的事情真是太棒了


let bg1 = null;

// simply store the value of a global object in the DOM
cy.window().then((win) => {
  bg1 = win.myPreciousGlobalObj.color;
});

// Do something that changes a global object
cy.get('a.dropdown-toggle').contains('File').click();

cy.window().then((win) => {
  const bg2 = win.myPreciousGlobalObj.color;
  // check if nodes and edges are loaded
  expect(bg1 != bg2).to.eq(true);
});

Here the interesting thing is even the things inside the then blocks are synchronous.这里有趣的是,即使是then块中的东西也是同步的。 This is so useful.这太有用了。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM