testing-library / dom-testing-library

🐙 Simple and complete DOM testing utilities that encourage good testing practices.
https://testing-library.com/dom
MIT License
3.26k stars 466 forks source link

`fireEvent.drop` API behaves not as expected about preventDefault in Browser and js env #1327

Open electroluxcode opened 1 month ago

electroluxcode commented 1 month ago

Reproduction:


import { fireEvent, render,act } from '@testing-library/react'
import type { FC } from 'react'

function tick(): Promise<void> {
    return new Promise((resolve) => {
        setTimeout(resolve, 0)
    })
}

async function fireDragDrop(source: Element) {
    await act(async () => {
        fireEvent.dragStart(source)
        fireEvent.dragEnter(source)
        fireEvent.dragOver(source)
        fireEvent.drop(source)
        await tick()
    })
}

const Container: FC = (function Container() {
    return (
    <>
        <div draggable="true"
        data-testid={'Other1'}
        onDragStart={(e)=>{
            e.preventDefault()
            console.log("ondragstart")
        }}
        onDragOver={()=>{
            console.log("onDragOver")
        }}>
        other1
        </div>
    </>
    )
})

describe('drag', () => {
    it('prevent', async () => {
        const rendered = render(<Container />)
        const box1 = (await rendered.findAllByTestId('Other1'))[0]
        await fireDragDrop(box1)
    })
})

What you did:

run it in brower and testing env

Problem description:

when I run this component in the browser, onDragOver isn't executed due to e.preventDefault()in the onDragStart test case,

but onDragOver is executed in testing env after onDragStart

Expect

ii should prevent onDragOver fn in test env

MatanBobi commented 1 month ago

Hi @electroluxcode, thanks for taking the time to open this. fireEvent is a very low level API, all it does is dispatch the event based on the configuration. I think you should be trying your use case with user-event instead. To top that, I'm not sure I understand the reasoning behind calling e.preventDefault() in onDragStart (afaik, usually calling e.preventDefault in drag scenarios only has meaning in onDragOver or onDragEnter to mark that an item can be dropped somewhere). I'm also not sure the browser's behavior is following the spec and I couldn't find any mention about that. Are you familiar with any spec that defines this behavior?

electroluxcode commented 1 month ago

Hi @electroluxcode, thanks for taking the time to open this. fireEvent is a very low level API, all it does is dispatch the event based on the configuration. I think you should be trying your use case with user-event instead. To top that, I'm not sure I understand the reasoning behind calling e.preventDefault() in onDragStart (afaik, usually calling e.preventDefault in drag scenarios only has meaning in onDragOver or onDragEnter to mark that an item can be dropped somewhere). I'm also not sure the browser's behavior is following the spec and I couldn't find any mention about that. Are you familiar with any spec that defines this behavior?

@MatanBobi Thank you very much for your reply. The reason I call e.preventDefault() in onDragStart is to avoid dragging some page elements that shouldn't be draggable. You can refer to

https://github.com/react-dnd/react-dnd/blob/7c88c37489a53b5ac98699c46a506a8e085f1c03/packages/backend-html5/src/HTML5BackendImpl.ts#L551-L553

This is a snippet of react-dnd code where this logic is encapsulated within the onDragStart method. I wanted to write test cases for this code snippet, but found that the onDragStart event behaves unexpectedly during testing.

and after reading your reply, I looked into user-event, but it seems there are no methods related to dragStart or dragOver.

and regarding the browser's behavior you mentioned, you can refer to https://html.spec.whatwg.org/multipage/dnd.html#dndevents.