测试 Cypress 中的 IonRange

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

Test IonRange in Cypress

问题

有趣的是,测试一个如此标准的组件中的如此标准的行为竟然如此困难。

在我的Ionic React组件中给定一个IonRange元素:

<IonRange
  min={0}
  max={30}
  onIonInput={() => console.log("changed")}
/>

当我手动测试IonRange时,我得到了预期的控制台输出。然而,当我像这样编写我的Cypress测试时(几乎直接从文档复制过来):

cy.get("ion-range")
  .as('range')
  .invoke('val', 10)
  .trigger('change')

我期望在输出中看到日志语句。

实际行为:范围旋钮跳到预期位置,但onIonInout回调从未被调用。

我还尝试了其他触发器,比如:

  .trigger('ion-change')
  .trigger('input')
  .trigger('ion-input')
  .trigger('range-change')
  .trigger('ionChange')
  .trigger('ionInput')

所有这些都导致相同的行为。

我还尝试了使用箭头键的解决方法。
这将是一个非常不令人满意的解决方法,因为它不适用于0到10,000的范围情况。我无论如何尝试了一下:

cy.get("ion-range").type('{rightArrow}') // 抛出“这是一个不可输入的元素”

范围旋钮跳到中间(奇怪)。

最后我采用的解决方法是:

cy.get("ion-range").click() // 在中心点击

(也被@SuchAnIgnorantThingToDo-UKR提到)
然后我期望值为15。但由于渲染分辨率的差异(即使有固定的视口),结果在15和16之间波动。此外,考虑到我们可能想要断言特定的值,这个解决方案也不令人满意。

我查看了以下内容:

如果您对这个非常常见的问题有任何想法,请告诉我。

英文:

It is interesting, that such standard behaviour in such a standard component is so hard to test.

Given an IonRange element in my Ionic React component:

&lt;IonRange
  min={0}
  max={30}
  onIonInput={()=&gt; console.log(&quot;changed&quot;)}
/&gt;

When I manually test the IonRange, I get the console output as expected.
However, when I have my cypress test like this (pretty much copied over from the docs):

cy.get(&quot;ion-range&quot;)
  .as(&#39;range&#39;)
  .invoke(&#39;val&#39;, 10)
  .trigger(&#39;change&#39;)

I expect to see the log statement in the output.

Actual behaviour: the range knob jumps to the expected position but the onIonInout callback is never invoked.

I also tried other triggers, such as

  .trigger(&#39;ion-change&#39;)
  .trigger(&#39;input&#39;)
  .trigger(&#39;ion-input&#39;)
  .trigger(&#39;range-change&#39;)
  .trigger(&#39;ionChange&#39;)
  .trigger(&#39;ionInput&#39;)

all leading to the same behaviour.

I also tried to workaround using arrow keys.
That would be a very unsatisfying solution, because it does not generalise for cases with ranges of, say, 0 to 10,000. I tried it anyway:

cy.get(&quot;ion-range&quot;).type(&#39;{rightArrow}&#39;) // trows &quot;this is a non-typable element&quot;

The range knob jumps to the centre (weirdly).

The workaround that I ended up with is:

cy.get(&quot;ion-range&quot;).click() // clicks in the centre 

(also mentioned by @SuchAnIgnorantThingToDo-UKR)
and then I expect the value 15. But due to differences in rendering resolutions (even with a set viewport), the result flakes between 15 and 16. Also, this solution is very unsatisfying given we might want to assert for specific values)

I reviewed the following:

Please let me know if you have any idea for this very trivial every-day problem.

答案1

得分: 2

The .click()命令将更改值并触发事件处理程序,但不如直接设置值精确。

cy.window()
  .its('console')
  .then((console) => {
    cy.spy(console, 'log').as('log')
  })

cy.get('ion-range')
  .click(300,0)

cy.get('@log') 
  .should('have.been.calledWith', 'changed', 31)
  .invoke('getCalls')
  .then((calls) => {
    // 打开DevTools以查看转储的表格
    console.table(calls)
    expect(calls[0].args).to.deep.eq(['changed', 31]) // 不精确,会有+1像素的差异
  })

来自

<IonRange onIonChange={({ detail })=> console.log("changed", detail.value)}></IonRange>

IonRange似乎具有不同的内部机制,与你引用的两个示例不同。

那两个示例底层使用<input>,但IonRange没有。相反,它是基于Stencil的Web组件,以其特定的方式响应事件。


范围旋钮跳到中心(奇怪)

我使用.click()但没有指定要单击的坐标时也遇到了相同的问题。我认为这是因为.type()内部包含了.click()

如果未指定,默认的单击位置是元素的中心。

英文:

The .click() command will alter the value and trigger the event handler, however it's not as precise as setting the value directly.

cy.window()
  .its(&#39;console&#39;)
  .then((console) =&gt; {
    cy.spy(console, &#39;log&#39;).as(&#39;log&#39;)
  })

cy.get(&#39;ion-range&#39;)
  .click(300,0)

cy.get(&#39;@log&#39;) 
  .should(&#39;have.been.calledWith&#39;, &#39;changed&#39;, 31)
  .invoke(&#39;getCalls&#39;)
  .then((calls) =&gt; {
    // open the DevTools to see the dumped table
    console.table(calls)
    expect(calls[0].args).to.deep.eq([&#39;changed&#39;, 31]) // not exact, a +1 pixel occurs
  })

from

&lt;IonRange onIonChange={({ detail })=&gt; console.log(&quot;changed&quot;, detail.value)}&gt;&lt;/IonRange&gt;

The IonRange seems to have a different internal mechanism to the two linked examples you cited.

Those have an &lt;input&gt; underlying, but IonRange does not have one. Instead, it's a stencil based web component which responds the events in it's own particular way.


The range knob jumps to the centre (weirdly)

I got the same with .click() but not specifying the coordinates to click at. I think that's because the .type() has a .click() built in.

The default click position, if not specified, is the center of the element.

答案2

得分: 1

@SuchAnIgnorantThingToDo-UKR 已经详细回答了。

我只想补充如何测试拖动。您可以这样做:

describe('Components', () => {
  it('ion-range', () => {
    cy.visit('https://ionicframework.com/docs/usage/v7/range/basic/demo.html?ionic:mode=ios')

    cy.get('ion-range').as('slider')

    const waitTime = 200

    cy.get('@slider') //
      .trigger('mouseenter')
      .wait(waitTime)
      .trigger('mousedown', 100, 0)
      .wait(waitTime)
      .trigger('mousemove', 150, 1, { force: true })
      .wait(waitTime)
      .trigger('mousemove', 200, 1, { force: true })
      .wait(waitTime)
      .trigger('mousemove', 300, 1, { force: true })
      .wait(waitTime)
      .trigger('mouseup', 300, 1)
  })
})
英文:

@SuchAnIgnorantThingToDo-UKR already answered in great detail.

I just want to add how to test the dragging. You can do this:

describe(&#39;Components&#39;, () =&gt; {
  it(&#39;ion-range&#39;, () =&gt; {
    cy.visit(&#39;https://ionicframework.com/docs/usage/v7/range/basic/demo.html?ionic:mode=ios&#39;)

    cy.get(&#39;ion-range&#39;).as(&#39;slider&#39;)

    const waitTime = 200

    cy.get(&#39;@slider&#39;) //
      .trigger(&#39;mouseenter&#39;)
      .wait(waitTime)
      .trigger(&#39;mousedown&#39;, 100, 0)
      .wait(waitTime)
      .trigger(&#39;mousemove&#39;, 150, 1, { force: true })
      .wait(waitTime)
      .trigger(&#39;mousemove&#39;, 200, 1, { force: true })
      .wait(waitTime)
      .trigger(&#39;mousemove&#39;, 300, 1, { force: true })
      .wait(waitTime)
      .trigger(&#39;mouseup&#39;, 300, 1)
  })
})

huangapple
  • 本文由 发表于 2023年6月13日 16:12:57
  • 转载请务必保留本文链接:https://go.coder-hub.com/76462916.html
匿名

发表评论

匿名网友

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

确定