英文:
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:
<IonRange
min={0}
max={30}
onIonInput={()=> console.log("changed")}
/>
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("ion-range")
.as('range')
.invoke('val', 10)
.trigger('change')
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('ion-change')
.trigger('input')
.trigger('ion-input')
.trigger('range-change')
.trigger('ionChange')
.trigger('ionInput')
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("ion-range").type('{rightArrow}') // trows "this is a non-typable element"
The range knob jumps to the centre (weirdly).
The workaround that I ended up with is:
cy.get("ion-range").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:
- This article is not helpful since I'm already using a plain number
- And this is kinda related but a different special case
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('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) => {
// open the DevTools to see the dumped table
console.table(calls)
expect(calls[0].args).to.deep.eq(['changed', 31]) // not exact, a +1 pixel occurs
})
from
<IonRange onIonChange={({ detail })=> console.log("changed", detail.value)}></IonRange>
The IonRange seems to have a different internal mechanism to the two linked examples you cited.
Those have an <input>
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('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)
})
})
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论