【问题标题】:How to implement drag and drop in cypress test?如何在 cypress 测试中实现拖放?
【发布时间】:2019-08-17 02:14:13
【问题描述】:

我正在努力使用CypressAngular Material Drag and Drop 测试拖放。因此,目标是将“开始工作”从待办事项转移到完成。 我创建了以下测试,应该可以让您轻松重现:

你可以玩 Stackblitz here

describe('Trying to implement drag-n-drop', () => {

    before(() => {
        Cypress.config('baseUrl', null);

        cy.viewport(1000, 600);
        cy.visit('https://angular-oxkc7l-zirwfs.stackblitz.io')
        .url().should('contain', 'angular')
        .get('h2').should('contain', 'To do');
    });

    it('Should work, based on this https://stackoverflow.com/a/54119137/3694288', () => {

        const dataTransfer = new DataTransfer;

        cy.get('#cdk-drop-list-0 > :nth-child(1)')
            .trigger('dragstart', { dataTransfer });

        cy.get('#cdk-drop-list-1')
            .trigger('drop', { dataTransfer });

        cy.get('#cdk-drop-list-0 > :nth-child(1)')
            .trigger('dragend');

        cy.get('#cdk-drop-list-1').should('contain', 'Get to work');
    });


    it('Should work, with this library https://github.com/4teamwork/cypress-drag-drop', () => {
        cy.get('#cdk-drop-list-0 > :nth-child(1)')
            .drag('#cdk-drop-list-1');

        cy.get('#cdk-drop-list-1').should('contain', 'Get to work');
    });

});

运行上述测试的结果如下:

这里是a repo 来开发解决方案。

感谢您的帮助。

事件触发,使用 chrome 调试器找到:

物品

  • 指针悬停
  • 指针输入
  • 鼠标悬停
  • 鼠标按下
  • 指针移动
  • 鼠标移动
  • 指针
  • 指针离开
  • 鼠标移出
  • 鼠标离开

拖放区

  • 指针悬停
  • 指针输入
  • 鼠标悬停
  • 指针移动
  • 鼠标移动
  • 指针离开
  • 鼠标移出
  • 鼠标离开

解决方案

在@Richard Matsen 的精彩回答之后,我最终添加了his answer 作为自定义命令。解决方案是这样的

support/drag-support.ts

    export function drag(dragSelector: string, dropSelector: string) {
        // Based on this answer: https://stackoverflow.com/a/55436989/3694288
        cy.get(dragSelector).should('exist')
          .get(dropSelector).should('exist');
    
          const draggable = Cypress.$(dragSelector)[0]; // Pick up this
          const droppable = Cypress.$(dropSelector)[0]; // Drop over this
    
          const coords = droppable.getBoundingClientRect();
          draggable.dispatchEvent(<any>new MouseEvent('mousedown'));
          draggable.dispatchEvent(<any>new MouseEvent('mousemove', {clientX: 10, clientY: 0}));
          draggable.dispatchEvent(<any>new MouseEvent('mousemove', {
              // I had to add (as any here --> maybe this can help solve the issue??)
              clientX: coords.left + 10,
              clientY: coords.top + 10  // A few extra pixels to get the ordering right
          }));
          draggable.dispatchEvent(new MouseEvent('mouseup'));
          return cy.get(dropSelector);
    }

support/commands.ts

    // Add typings for the custom command
    declare global {
        namespace Cypress {
            interface Chainable {
                drag: (dragSelector: string, dropSelector: string) => Chainable;
            }
        }
    }
    // Finally add the custom command
    Cypress.Commands.add('drag', drag);

在规范文件中

    it('???? Thx to Stackoverflow, drag and drop support now works ????', () => {
       cy.drag('#cdk-drop-list-0 > :nth-child(1)', '#cdk-drop-list-1')
       .should('contain', 'Get to work');
    });

一个小的 giph,因为我很高兴它终于可以工作了????

CI

现在它也适用于 CI ???? (和本地电子)。使用 CircleCI 2.0 测试。

【问题讨论】:

  • 我还没有测试过,但(可能)function drag 中的cy.get(dragSelector).should('exist') 不会防止异步加载,命令只是告诉赛普拉斯“将此测试放入队列并尽快运行” ,然后代码继续下一行(同步,因此立即执行)。您可以使用嵌套的then()s 来防止这种情况发生,或者可能是在同步代码周围使用cy.wrap().then() 将其变成排队块。
  • 感谢@RichardMatsen 的评论。因为赛普拉斯在大多数情况下“正常工作”,所以我的许多测试中通常只有.get()。感谢您的输入,我将测试拖动方法的异步性质。我也一直在想drag方法实际上应该只有目标作为输入,源应该在chain中提供:)
  • 这是否已经用 iframe 中的元素进行了测试?
  • 否 - 这不是在 iframe 中完成的。
  • 这似乎不再起作用了。您是否熟悉最新 Angular 和 Cdk 拖放的任何必要更改?

标签: angular cypress angular-material2


【解决方案1】:

调度 MouseEvents 似乎是测试 Angular Material 拖放的唯一方法。

您还应该注意以下问题,该问题在 Protractor 中进行测试,但也适用于此 Cypress 测试

CDK DragDrop Regression between 7.0.0-beta.2 and 7.0.0-rc.2: Protractor tests stopped working #13642,

似乎(为了更好的解释)需要对鼠标移动进行额外的轻推。

workaround(量角器语法)形式给出的步骤,

private async dragAndDrop ( $element, $destination ) {
  await browser.actions().mouseMove( $element ).perform();
  await browser.actions().mouseDown( $element ).perform();
  await browser.actions().mouseMove( {x: 10, y: 0 } ).perform();
  await browser.actions().mouseMove( $destination ).perform();
  return browser.actions().mouseUp().perform();
}

可以翻译成Cypress测试,我找到的最简单的形式是

it('works (simply)', () => {
  const draggable = Cypress.$('#cdk-drop-list-0 > :nth-child(1)')[0]  // Pick up this
  const droppable = Cypress.$('#cdk-drop-list-1 > :nth-child(4)')[0]  // Drop over this

  const coords = droppable.getBoundingClientRect()
  draggable.dispatchEvent(new MouseEvent('mousedown'));
  draggable.dispatchEvent(new MouseEvent('mousemove', {clientX: 10, clientY: 0}));
  draggable.dispatchEvent(new MouseEvent('mousemove', {
    clientX: coords.x+10,   
    clientY: coords.y+10  // A few extra pixels to get the ordering right
  }));
  draggable.dispatchEvent(new MouseEvent('mouseup'));

  cy.get('#cdk-drop-list-1').should('contain', 'Get to work');
  cy.get('#cdk-drop-list-1 > .cdk-drag').eq(3).should('contain', 'Get to work');

});

注意事项

  • 引用问题中的问题不仅限于 Protractor。如果您在 Cypress 测试中删除第一个 mousemove,它也会失败。
  • cy.get(..).trigger() 语法似乎不适用于 Angular,但原生 dispatchEvent() 可以。
  • 在目标列表中的特定元素上拖动(而不是仅仅放在列表上)可以在目标列表中精确定位。
  • dragstart, dragend 可能不适合 Angular Material,因为代码显示接收到的事件是 CdkDragDrop 类型而不是 DataTransfer 对象。
  • 如果内容是异步获取的,您可能必须从 Cypress.$(...) 切换到 cy.get(...).then(el =&gt; {...}),以利用 cypress 在命令中的自动重试。
  • 我必须添加 10 秒超时才能访问 Stackblitz 网址。

异步列表获取

如果列表在组件构建期间由异步 Angular 服务 (httpClient) 获取,则在测试中使用它

const draggable = Cypress.$('#cdk-drop-list-0 > :nth-child(1)')[0]

不会起作用,因为第 n 个孩子不会立即出现,只有在 fetch 完成后才会出现。

相反,您可以使用cy.get() 提供重试,直至超时(默认为 5 秒)。

cy.get('#cdk-drop-list-0 > :nth-child(1)').then(el => {
  const draggable = el[0]  // Pick up this
  cy.get('#cdk-drop-list-1 > :nth-child(4)').then(el => {
    const droppable = el[0]  // Drop over this

    const coords = droppable.getBoundingClientRect()
    draggable.dispatchEvent(new MouseEvent('mousemove'));
    draggable.dispatchEvent(new MouseEvent('mousedown'));
    draggable.dispatchEvent(new MouseEvent('mousemove', {clientX: 10, clientY: 0}));
    draggable.dispatchEvent(new MouseEvent('mousemove', {clientX: coords.x+10, clientY: coords.y+10}));
    draggable.dispatchEvent(new MouseEvent('mouseup'));

  })

  cy.get('#cdk-drop-list-1').should('contain', 'Get to work');
  cy.get('#cdk-drop-list-1 > .cdk-drag').eq(3).should('contain', 'Get to work');
})

或者我的偏好是使用“金丝雀”测试来确保加载完成,例如

before(() => {
  cy.get('#cdk-drop-list-0 > :nth-child(1)') // Canary - wait 5s for data
})

it('should...', () => {
  const draggable = Cypress.$('#cdk-drop-list-0 > :nth-child(1)')[0]  // Pick up this
  const droppable = Cypress.$('#cdk-drop-list-1 > :nth-child(4)')[0]  // Drop over this
  ...
})

打字稿支持

警告 - 这是解决 Typescript 编译器问题的快速方法,可以改进。

const coords: ClientRect = droppable.getBoundingClientRect()
draggable.dispatchEvent(new (<any>MouseEvent)('mousemove'));
draggable.dispatchEvent(new (<any>MouseEvent)('mousedown'));
draggable.dispatchEvent(new (<any>MouseEvent)('mousemove', {clientX: 10.0, clientY: 0.0}));
draggable.dispatchEvent(new (<any>MouseEvent)('mousemove', {clientX: coords.left + 10.0, clientY: coords.top + 10.0}));
draggable.dispatchEvent(new (<any>MouseEvent)('mouseup'));

【讨论】:

  • 嗨,Richard - 该解决方案效果很好。 Cypress.$(...)cy.get(...) 有什么区别?我没见过Cypress以前那样用过吗?
  • Cypress.$(...) 是在赛普拉斯全局对象上公开的 jquery。如果结果未定义(即选择器不存在),它会返回结果但不会使测试失败。如果您通过 http 获取列表项,则可能会发生这种情况(在您的示例中,列表是硬编码的,因此可以正常工作)。另一方面,cy.get(...) 基本上重复Cypress.$(...) 直到它成功,或者直到超时,此时测试失败。使用cy.get(...)的代价是需要在.then(...)内嵌套下游代码。
  • 我将添加几个用于异步列表获取的示例。
  • 感谢您的精彩解释,很有道理。我刚刚意识到不幸的是,该解决方案在 CI 中不起作用。我在本地尝试使用 Electron,但在这里它也失败了。您知道错误TypeError: Failed to construct 'MouseEvent': The provided double value is non-finite. 的原因可能是什么吗?
  • 这是一个有趣的错误信息。我会看看(可能必须在明天)。如果您可以更新您的回购协议,那将给我一个良好的开端。
【解决方案2】:

我已经写了一个关于如何实现拖放的小例子。

它通过像这样添加dragTo 命令来工作:

/// <reference types="cypress"/>

it('works', () => {
  cy.visit('https://angular-oxkc7l-zirwfs.stackblitz.io/')
  cy.contains('To do', { timeout: 15000 }) // ensure page is loaded -__-

  const item = '.example-box:not(.cdk-drag-placeholder)'

  cy.get('#cdk-drop-list-1').children(item).should('have.length', 5)

  cy.get('.example-box:contains("Get to work")').dragTo('.example-box:contains("Get up")')
  cy.get('#cdk-drop-list-1').children(item).should('have.length', 6)

  // interpolates 10 extra mousemove events on the way
  cy.get('#cdk-drop-list-0').dragTo('#cdk-drop-list-1', { steps: 10 })
  cy.get('#cdk-drop-list-1').children(item).should('have.length', 7)

  // sets steps >= 10
  cy.get('#cdk-drop-list-0').dragTo('#cdk-drop-list-1', { smooth: true })
  cy.get('#cdk-drop-list-1').children(item).should('have.length', 8)

  cy.get('#cdk-drop-list-0').dragTo('#cdk-drop-list-1')
  cy.get('#cdk-drop-list-1').children(item).should('have.length', 9)
})

要添加它,请尝试将其放入您的 support/index.js 或将其粘贴到规范文件的底部(警告:代码质量差):


const getCoords = ($el) => {
  const domRect = $el[0].getBoundingClientRect()
  const coords = { x: domRect.left + (domRect.width / 2 || 0), y: domRect.top + (domRect.height / 2 || 0) }

  return coords
}

const dragTo = (subject, to, opts) => {

  opts = Cypress._.defaults(opts, {
    // delay inbetween steps
    delay: 0,
    // interpolation between coords
    steps: 0,
    // >=10 steps
    smooth: false,
  })

  if (opts.smooth) {
    opts.steps = Math.max(opts.steps, 10)
  }

  const win = subject[0].ownerDocument.defaultView

  const elFromCoords = (coords) => win.document.elementFromPoint(coords.x, coords.y)
  const winMouseEvent = win.MouseEvent

  const send = (type, coords, el) => {

    el = el || elFromCoords(coords)

    el.dispatchEvent(
      new winMouseEvent(type, Object.assign({}, { clientX: coords.x, clientY: coords.y }, { bubbles: true, cancelable: true }))
    )
  }

  const toSel = to

  function drag (from, to, steps = 1) {

    const fromEl = elFromCoords(from)

    const _log = Cypress.log({
      $el: fromEl,
      name: 'drag to',
      message: toSel,
    })

    _log.snapshot('before', { next: 'after', at: 0 })

    _log.set({ coords: to })

    send('mouseover', from, fromEl)
    send('mousedown', from, fromEl)

    cy.then(() => {
      return Cypress.Promise.try(() => {

        if (steps > 0) {

          const dx = (to.x - from.x) / steps
          const dy = (to.y - from.y) / steps

          return Cypress.Promise.map(Array(steps).fill(), (v, i) => {
            i = steps - 1 - i

            let _to = {
              x: from.x + dx * (i),
              y: from.y + dy * (i),
            }

            send('mousemove', _to, fromEl)

            return Cypress.Promise.delay(opts.delay)

          }, { concurrency: 1 })
        }
      })
      .then(() => {

        send('mousemove', to, fromEl)
        send('mouseover', to)
        send('mousemove', to)
        send('mouseup', to)
        _log.snapshot('after', { at: 1 }).end()

      })

    })

  }

  const $el = subject
  const fromCoords = getCoords($el)
  const toCoords = getCoords(cy.$$(to))

  drag(fromCoords, toCoords, opts.steps)
}

Cypress.Commands.addAll(
  { prevSubject: 'element' },
  {
    dragTo,
  }
)

【讨论】:

  • 嗨@bkucera 看起来很棒。我正在尝试将其集成到我的项目中,但我们使用的是打字稿。 dragTo 方法出现多个错误。您愿意帮助解决问题吗?第一个是这一行:return Cypress.Promise.try(() =&gt; {返回Argument of type '() =&gt; Bluebird&lt;void[]&gt; | undefined' is not assignable to parameter of type '() =&gt; void[] | PromiseLike&lt;void[]&gt;'.
  • 这可能需要留给读者作为练习。或者干脆将文件保存在 js 中,并在 Cypress 中添加自定义命令的类型定义
  • 嗨@bkucera 你的方法是唯一对我有用的方法!为了让它工作,我必须设置一些延迟,步骤和平滑:{延迟:50,步骤:30,平滑:真}。即使是上面 Richard matsem 的方法也不适合我。非常感谢您的帮助
  • 嗨@bkucera 实现了您的代码并且它正在工作。我面临的另一个问题是强行拖动元素并插入。该解决方案是否已在您的代码中实现??
  • 任何指导如何使用打字稿?
【解决方案3】:

经过大量的斗争,我设法使拖放工作:

cy.get('.list .item')
      .contains(startpos)
      .trigger('dragstart', { dataTransfer: new DataTransfer });
cy.get('.list .item')
      .eq(endpos)
      .trigger('drop')
      .trigger('dragend');

非常容易使用。

【讨论】:

  • startpos 和 endpos 应该是什么?
  • 在我的示例中,startpos 是与我要移动的项目中的文本匹配的字符串,endpos 是列表中的位置我想放弃它。
  • 这应该适用于角材料拖放吗?
  • 适用于 React 和一些疯狂的东西,所以我认为它应该在任何地方都能正常工作,谢谢!
【解决方案4】:

如果有人还在为 cdkDropListEntered 未触发而苦苦挣扎,您可能需要检查是否有任何滚动正在进行。

由于滚动是由 CDK 处理的(例如https://github.com/angular/components/blob/master/src/cdk/scrolling/viewport-ruler.ts#L131),我必须将滚动位置添加到任何鼠标事件中。滚动位置是这样计算的(对应于上面的链接):

const win = subject[0].ownerDocument.defaultView;
const window = win;
const document = window.document;
const documentElement = document.documentElement;
const documentRect = documentElement.getBoundingClientRect();

const top =
  -documentRect.top ||
  document.body.scrollTop ||
  window.scrollY ||
  documentElement.scrollTop ||
  0;

const left =
  -documentRect.left ||
  document.body.scrollLeft ||
  window.scrollX ||
  documentElement.scrollLeft ||
  0;

其中 subject 是产生的结果,例如通过 cy.get 命令。

【讨论】:

    【解决方案5】:

    您只需替换要拖放的类或 id


    const dataTransfer = new DataTransfer(); 
    cy.get('ID or class which you want to drag').trigger('dragstart',{
       dataTransfer 
      });
    cy.get('ID or class where  you want to drop').trigger('drop',{
         dataTransfer
    });
    

    【讨论】:

      【解决方案6】:

      如果拖放之间不涉及任何步骤 然后使用以下步骤:

      下载这个以运行具有拖放功能的脚本

      npm install --save-dev @4tw/cypress-drag-drop

      转到 cypress.json

      "compilerOptions": {
                  "types": ["cypress", "@4tw/cypress-drag-drop"]
                  }
      

      在 cypress.json 上添加这个

      继续 command.js

       require('@4tw/cypress-drag-drop')
      

      使用

      cy.get('Class or id which you want to drag ').drag('Class or id where you want to drop')
      

      【讨论】:

        【解决方案7】:

        你有没有看official recipe 的功能完全一样?

        它使用这种触发事件的组合

        cy.get('.selector')
          .trigger('mousedown', { which: 1 })
          .trigger('mousemove', { clientX: 400, clientY: 500 })
          .trigger('mouseup', {force: true})
        

        要拖放项目,请在尝试后告诉我是否需要更多帮助?

        【讨论】:

        • 我在推特上向 Bahmutov 寻求帮助,他建议我实现那里的代码 sn-p。但我坚持使用鼠标移动。会喜欢你的帮助:) twitter.com/bahmutov/status/1110589982241161217
        • 你能分享一个存储库吗?所以我们可以轻松分享各种尝试?
        • 我正在尝试,但我认为问题在于仔细识别哪个元素被监听以调度哪个事件?
        • 感谢您的帮助。我使用了 chrome 开发工具getEventListeners 来检查放置区,这里有一个事件被列为“cdkDropListDropped”。所以我想知道我是否应该触发这样的事件。对自定义事件没有太多经验,但请阅读它。并感谢朋友的帮助!
        • 我认为您不需要触发自定义事件,自定义事件通常由库内部使用。困难的部分是要了解谁在听哪个事件,我的意思是:可能mousedown 事件已经在项目本身上听了。 mousemove 事件没有监听它,也没有监听容器......但可能是整个放置区(如果不是页面),因为您可以将项目从其容器中拖出......和 ​​mousedown 事件。 ..我不知道确切,它也需要被发现。看看源代码,可能会有所帮助?
        【解决方案8】:

        不是 Angular 特定的,但应该是通用且足够简单的,可以在需要时进行调整。 我确实尝试了很多食谱,也尝试了cypress-file-upload,但是这不适用于 webp。

        下面的命令似乎适用于大多数情况,并且非常准确地反映了用户会做什么

        Cypress.Commands.add('dropFile', {prevSubject: true}, (subject, fileName, fileType) => {
          return cy.fixture(fileName, 'binary').then((data) => {
            return Cypress.Blob.binaryStringToBlob(data, fileType).then(blob => {
              const file = new File([blob], fileName, {type: fileType});
              const dataTransfer = new DataTransfer();
              dataTransfer.items.add(file);
              cy.wrap(subject)
                .trigger("dragenter", {force: true})
                .trigger("drop", {dataTransfer})
            })
          })
        })
        

        确保在您的 cypress.json 配置文件中指定 fixturesFolder。然后你只需像下面这样使用

        cy.get("#dropzone").dropFile("myfile1.webp", "image/webp")
        cy.get("#dropzone").dropFile("myfile2.jpg", "image/jpeg")
        

        【讨论】:

          【解决方案9】:

          这是我的 cypress 命令:

          Cypress.Commands.add(
            'dragTo',
            (selector: string, position: { x: number; y: number }) => {
              const log = Cypress.log({
                message: `Drag ${selector} to (${position.x}, ${position.y})`,
                consoleProps: () => ({ selector, position })
              });
              log.snapshot('before');
              const ret = cy
                .get(selector, { log: false })
                .trigger('mouseover', { force: true, log: false })
                .trigger('mousedown', {
                  button: 0,
                  log: false
                })
                .trigger('mousemove', {
                  pageX: 10,
                  pageY: 10,
                  log: false
                })
                .then(el => {
                  log.snapshot('Drag start');
                  return el;
                })
                .trigger('mousemove', {
                  pageX: position.x,
                  pageY: position.y,
                  force: true,
                  log: false
                })
                .then(event => {
                  log.snapshot('Drag End');
                  return event;
                })
                .trigger('mouseup', { force: true, log: false })
                .then(() => {
                  log.snapshot('after');
                });
              log.end();
              return ret;
            }
          );
          

          【讨论】:

            【解决方案10】:

            试试这个:

              it('should drag and drop the element', () => {
                const myItem = cy.get('my-item').first();
                myItem.trigger('mousedown', 100, 100, { force: true }).trigger('mousemove', 300, 300, { force: true });
                myItem.click().trigger('mouseup', { force: true });
            });
            

            【讨论】:

              【解决方案11】:

              我一直遇到 dropevent.isPointerOverContainer 的问题,这里的其他解决方案总是错误的,所以最后我不得不使用 click(),而不是 mouseup。这是使指针位置和拖动位置处于正确位置以在我的组件中触发 drop() 事件的唯一方法。

              export function drag(dragSelector: string, dropSelector: string) {
                // Based on this answer: https://stackoverflow.com/questions/55361499/how-to-implement-drag-and-drop-in-cypress-test
                cy.get(dragSelector).should('exist').get(dropSelector).should('exist');
              
                const draggable = Cypress.$(dragSelector)[0]; // Pick up this
                const droppable = Cypress.$(dropSelector)[0]; // Drop over this
                const coords = droppable.getBoundingClientRect();
              
                draggable.dispatchEvent(<any>new MouseEvent('mousedown'));
                draggable.dispatchEvent(<any>new MouseEvent('mousemove', {clientX: 10, clientY: 0}));
                draggable.dispatchEvent(<any>new MouseEvent('mousemove', {clientX: coords.left + 40, clientY: coords.top + 10}));
                cy.get(dropSelector).click();
                //   draggable.dispatchEvent(new MouseEvent('mouseup'));
              
                return cy.get(dropSelector);
              }
              
              // Add typings for the custom command
              declare global {
                namespace Cypress {
                  interface Chainable {
                    drag: (dragSelector: string, dropSelector: string) => Chainable;
                  }
                }
              }
              // Finally add the custom command
              Cypress.Commands.add('drag', drag);
              

              【讨论】:

                【解决方案12】:

                对于那些在拖放和“react-beautiful-dnd”库中苦苦挣扎的人,这里有一段代码对我有帮助(没有其他帮助)。提取自this post

                Cypress.Commands.add('dragAndDrop', (subject, target) => {
                Cypress.log({
                    name: 'DRAGNDROP',
                    message: `Dragging element ${subject} to ${target}`,
                    consoleProps: () => {
                        return {
                            subject: subject,
                            target: target
                        };
                    }
                });
                const BUTTON_INDEX = 0;
                const SLOPPY_CLICK_THRESHOLD = 10;
                cy.get(target)
                    .first()
                    .then($target => {
                        let coordsDrop = $target[0].getBoundingClientRect();
                        cy.get(subject)
                            .first()
                            .then(subject => {
                                const coordsDrag = subject[0].getBoundingClientRect();
                                cy.wrap(subject)
                                    .trigger('mousedown', {
                                        button: BUTTON_INDEX,
                                        clientX: coordsDrag.x,
                                        clientY: coordsDrag.y,
                                        force: true
                                    })
                                    .trigger('mousemove', {
                                        button: BUTTON_INDEX,
                                        clientX: coordsDrag.x + SLOPPY_CLICK_THRESHOLD,
                                        clientY: coordsDrag.y,
                                        force: true
                                    });
                                cy.get('body')
                                    .trigger('mousemove', {
                                        button: BUTTON_INDEX,
                                        clientX: coordsDrop.x,
                                        clientY: coordsDrop.y,
                                        force: true            
                                    })
                                    .trigger('mouseup');
                            });
                    });
                });
                

                【讨论】:

                  【解决方案13】:

                  @bkucera 的代码对我来说有效,谢谢。但我必须尝试 3 件事才能按预期为我工作:

                  1.

                  let _to = {
                                x: from.x + dx * (i),
                                y: from.y - dy * (i),
                              }
                  
                  1. i = i+1

                  2. 不得不使用一些延迟、步骤和平滑:true

                  delay: 30,
                  steps: 30,
                  smooth: true,
                  

                  【讨论】:

                    猜你喜欢
                    • 2020-05-27
                    • 2023-02-13
                    • 2022-06-29
                    • 1970-01-01
                    • 2019-02-24
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    • 1970-01-01
                    相关资源
                    最近更新 更多