Cypress.io – 如何等待返回已调用属性的方法结果?

huangapple go评论73阅读模式
英文:

Cypress.io - How do I wait for the result of a method that returns an invoked attribute?

问题

I have translated the code portions you provided. Here they are:

我有一个方法,它获取元素`hrefAppTheme`href属性并检查它是否是字符串数组`appThemes`中的一个值:

```ts
describe('...', () => {
    it('应该...', () => {
      (...)
      let defaultAppTheme = '';
      onMenuPage.hrefAppTheme.invoke('attr', 'href')
        .then(hrefVal => {
          if (typeof hrefVal !== 'undefined') {
            expect(hrefVal).to.be.oneOf(onMenuPage.appThemes);
            defaultAppTheme = hrefVal;

      //在下面进一步引用变量'defaultAppTheme'...
    }
  });

可以安全地假设this.hrefAppTheme.invoke('attr', 'href')始终返回用户主题之一(一个字符串),因为上面的代码可靠工作。

由于事实证明我需要在多个不同的地方使用这个逻辑,我想将它提取到一个方法中并放入页面对象类中。这是我得到的:

export default class MenuPage {
  (...)
  getUserTheme(): string {
    let userTheme = '';
    cy.then(() => {
      this.hrefAppTheme.invoke('attr', 'href')
        .then(resVal => {
          if (typeof resVal !== 'undefined') {
            userTheme = resVal;
          }
        });
    });
    return userTheme;
  }

我发现我需要使用cy.wrap()方法来分配被返回的字符串:

describe('...', () => {
    it('应该...', () => {
      (...)
      let defaultAppTheme = '';
      cy.wrap(onMenuPage.getUserTheme()).then(resVal => {
        defaultAppTheme = resVal;
        expect(defaultAppTheme).to.be.oneOf(onMenuPage.appThemes);
      });

      //在下面进一步引用变量'defaultAppTheme'...

但是,这里有些问题,因为返回的值始终是一个空字符串,而不是已解析的值:

AssertionError: 期望''为[ 'theme-light.css', 'theme-dark.css' ]之一
      + 期望 - 实际

      +[ 'theme-light.css', 'theme-dark.css' ]

如何返回myWebelement.invoke('attr', 'href').then(hrefVal => {中的解析值,并如何通过调用此方法将其分配给变量?

或者是否有更好的方法来提取工作的代码到一个方法中?


<details>
<summary>英文:</summary>

I have a method that gets the href-attribute of an element `hrefAppTheme` and checks if its one of the values of the string-array `appThemes`:

```ts
describe(&#39;...&#39;,() =&gt; {
    it(&#39;should ...&#39;, () =&gt; {
      (...)
      let defaultAppTheme = &#39;&#39;;
      onMenuPage.hrefAppTheme.invoke(&#39;attr&#39;, &#39;href&#39;)
        .then(hrefVal =&gt; {
          if (typeof hrefVal !== &#39;undefined&#39;) {
            expect(hrefVal).to.be.oneOf(onMenuPage.appThemes);
            defaultAppTheme = hrefVal;

      //referencing the variable &#39;defaultAppTheme&#39; further below...
    }
  });

It is safe to assume that this.hrefAppTheme.invoke(&#39;attr&#39;, &#39;href&#39;) always returns one of the user-themes (a string), because the code above works reliably.

Since it turned out that I need to use that logic in several different places, I want to extract it into a method and put it into the page-object-class. This is what I've got:

export default class MenuPage {
  (...)
  getUserTheme(): string {
    let userTheme = &#39;&#39;;
    cy.then(() =&gt; {
      this.hrefAppTheme.invoke(&#39;attr&#39;, &#39;href&#39;)
        .then(resVal =&gt; {
          if (typeof resVal !== &#39;undefined&#39;) {
            userTheme = resVal;
          }
        });
    });
    return userTheme;
  }

I figured that I need to use the cy.wrap()-method to assign the string that is being returned:

describe(&#39;...&#39;,() =&gt; {
    it(&#39;should ...&#39;, () =&gt; {
      (...)
      let defaultAppTheme = &#39;&#39;;
      cy.wrap(onMenuPage.getUserTheme()).then(resVal =&gt; {
        defaultAppTheme = resVal;
        expect(defaultAppTheme).to.be.oneOf(onMenuPage.appThemes);
      });

      //referencing the variable &#39;defaultAppTheme&#39; further below...

However, there is something wrong with this, because the value being returned is always an empty string, not the resolved-value:

AssertionError: expected &#39;&#39; to be one of [ &#39;theme-light.css&#39;, &#39;theme-dark.css&#39; ]
      + expected - actual

      +[ &#39;theme-light.css&#39;, &#39;theme-dark.css&#39; ]

Cypress.io – 如何等待返回已调用属性的方法结果?

How do you return the resolve-value of myWebelement.invoke(&#39;attr&#39;, &#39;href&#39;).then(hrefVal =&gt; {(...), and how can you assign it to a variable by calling this method?

Or is there a better approach to extract the working code into a method?

答案1

得分: 6

The this.hrefAppTheme 已经是一个 Chainer,因为它可以在之后使用 .invoke('attr', 'href')。您可以简化,只需返回它。

接下来的 .then(resVal...) 都是关于修改 resVal。将你的 userTheme 变量放在其中并返回它。返回值会修改前面 Chainer 的结果。

export default class MenuPage {
  (...)
  getUserTheme(): Chainable&lt;string&gt; {
    
    return this.hrefAppTheme.invoke('attr','href') // 返回整个链
      .then(resVal => {
        let userTheme = '';
        if (typeof resVal !== 'undefined') {
          userTheme = resVal;
        }
        return userTheme;   // 在这里返回以修改 Chainer 结果
      });
  }

然后在测试中,像任何自定义命令一样使用它

describe('...',() => {
    it('should ...', () => {

      onMenuPage.getUserTheme().then(defaultAppTheme => {
        expect(defaultAppTheme).to.be.oneOf(onMenuPage.appThemes);
      });

AssertionError: 期望 ' ' 是 [ 'theme-light.css', 'theme-dark.css' ] 中的一个

现在您可能可以看到为什么会发生这个错误,因为当 resVal 为 undefined 时,返回值是 userTheme = ''

可以通过将一个预期的字符串作为默认值返回来修复这个问题:

export default class MenuPage {
  (...)
  getUserTheme(): Chainable&lt;string&gt; {
    
    return this.hrefAppTheme.invoke('attr','href') 
      .then(resVal => {
        let userTheme = 'theme-light.css';        // 更改默认值
        if (typeof resVal !== 'undefined') {
          userTheme = resVal;
        }
        return userTheme;  
      });
  }
英文:

The this.hrefAppTheme is already a Chainer, since it can use .invoke(&#39;attr&#39;, &#39;href&#39;) after it. You can simplify by just returning it.

The .then(resVal...) that follows is all about modifying the resVal. Move your userTheme var inside there and return it. The return value modifies the result of the previous chainers.

export default class MenuPage {
  (...)
  getUserTheme(): Chainable&lt;string&gt; {
    
    return this.hrefAppTheme.invoke(&#39;attr&#39;,&#39;href&#39;) // return the whole chain
      .then(resVal =&gt; {
        let userTheme = &#39;&#39;;
        if (typeof resVal !== &#39;undefined&#39;) {
          userTheme = resVal;
        }
        return userTheme;   // return here to modify the chainer result
      });
  }

Then in the test, just use it like any Custom Command

describe(&#39;...&#39;,() =&gt; {
    it(&#39;should ...&#39;, () =&gt; {

      onMenuPage.getUserTheme().then(defaultAppTheme =&gt; {
        expect(defaultAppTheme).to.be.oneOf(onMenuPage.appThemes);
      });

AssertionError: expected '' to be one of [ 'theme-light.css', 'theme-dark.css' ]

You can probably now see why that error occurs, since the return value is userTheme = &#39;&#39; when the resVal is undefined.

That can be fixed by returning one on the expcted strings as default:

export default class MenuPage {
  (...)
  getUserTheme(): Chainable&lt;string&gt; {
    
    return this.hrefAppTheme.invoke(&#39;attr&#39;,&#39;href&#39;) 
      .then(resVal =&gt; {
        let userTheme = &#39;theme-light.css&#39;;        // change default value
        if (typeof resVal !== &#39;undefined&#39;) {
          userTheme = resVal;
        }
        return userTheme;  
      });
  }

答案2

得分: 2

Because of the asynchronous nature of Cypress commands, the return in your function is occurring before the Cypress commands have set the value. In order to return the value, you will have to return the Cypress command chain that is getting the value.

getUserTheme(): string {
    return cy.then(() => {
      return this.hrefAppTheme.invoke('attr', 'href')
        .then(resVal => {
          return resVal ?? '';
        });
    })
  }

However, this does not return a string variable, but a Chainable<string>. This means you won't have to use cy.wrap() around the variable, but can just continue the chain.

onMenuPage.getUserTheme().then(resVal => {
  expect(resVal).to.be.oneOf(onMenuPage.appThemes);
});
英文:

Because of the asynchronous nature of Cypress commands, the return in your function is occurring before the Cypress commands have set the value. In order to return the value, you will have to return the Cypress command chain that is getting the value.

getUserTheme(): string {
    return cy.then(() =&gt; {
      return this.hrefAppTheme.invoke(&#39;attr&#39;, &#39;href&#39;)
        .then(resVal =&gt; {
          return resVal ?? &#39;&#39;;
        });
    })
  }

However, this does not return a string variable, but a Chainable&lt;string&gt;. This means you won't have to use cy.wrap() around the variable, but can just continue the chain.

onMenuPage.getUserTheme().then(resVal =&gt; {
  expect(resVal).to.be.oneOf(onMenuPage.appThemes);
});

huangapple
  • 本文由 发表于 2023年6月8日 22:33:56
  • 转载请务必保留本文链接:https://go.coder-hub.com/76432951.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定