uppy/examples/react/test/index.test.tsx
Prakash d6b3aa575e
@uppy/components: fix dropzone global id (#5967)
fixes #5946 

Root Cause : 

`createDropzone` used a single global `id` for the file input
(`'uppy-dropzone-file-input'`) Clicking any `<Dropzone />` did
`document.getElementById(<global-id>).click()`, which always targeted
the first input in the DOM, so files were added to the first Uppy
instance.


9bac4c8398/packages/%40uppy/components/src/hooks/dropzone.ts (L73-L77)


**Solutions :**

Simplest solution would have been to just make the input id unique per
Uppy instance using `ctx.uppy.getID()`, and click that specific input.

```typescript

const fileInputId = 'uppy-dropzone-file-input-' + ctx.uppy.getID()

```
  **Caveats**:
  
If users don’t pass a custom id to `new Uppy()`, all instances default
to uppy, so ids still collide across instances.
  Multiple Dropzones under one instance still share the same id.
  

Switched to a ref-based approach so clicks trigger the input directly,
without relying on `document.getElementById` lookups. It still falls
back to a DOM click for backward compatibility.

**StackBlitz Link :** 


https://stackblitz.com/github/qxprakash/uppy/tree/debug_dropzone/examples/react?file=package.json&embed=1&view=editor&showSidebar=1&hideTerminal=1

  


**Update: Went for the ID based solution upon discussion with the team
as it's simpler.**
2025-09-17 10:39:57 +02:00

124 lines
4.1 KiB
TypeScript

import { userEvent } from '@vitest/browser/context'
import { describe, expect, test } from 'vitest'
import { render } from 'vitest-browser-react'
import App from '../src/App'
const createMockFile = (name: string, type: string, size: number = 1024) => {
return new File(['test content'], name, { type })
}
describe('App', () => {
test('renders all main sections and upload button is initially disabled', async () => {
const screen = render(<App />)
await expect.element(screen.getByText('With list')).toBeInTheDocument()
await expect.element(screen.getByText('With grid')).toBeInTheDocument()
await expect
.element(screen.getByText('With custom dropzone'))
.toBeInTheDocument()
const uploadButton = screen.getByRole('button', { name: /upload/i })
await expect.element(uploadButton).toBeInTheDocument()
await expect.element(uploadButton).toBeDisabled()
})
test('can add and remove files and upload', async () => {
const screen = render(<App />)
const fileInput = document.getElementById(
'uppy-dropzone-file-input-uppy',
) as Element
await userEvent.upload(fileInput, createMockFile('test.txt', 'text/plain'))
// for list and grid
for (const element of screen.getByText('test.txt').elements()) {
await expect.element(element).toBeInTheDocument()
}
await screen.getByText('remove').first().click()
for (const element of screen.getByText('test.txt').elements()) {
await expect.element(element).not.toBeInTheDocument()
}
await userEvent.upload(fileInput, createMockFile('test.txt', 'text/plain'))
await screen.getByRole('button', { name: /upload/i }).click()
await expect
.element(screen.getByRole('button', { name: /complete/i }))
.toBeInTheDocument()
})
})
describe('ScreenCapture Component', () => {
test('renders with title, control buttons, and close functionality works', async () => {
const screen = render(<App />)
await screen.getByRole('button', { name: 'Screen Capture' }).click()
await expect
.element(screen.getByRole('heading', { name: 'Screen Capture' }))
.toBeInTheDocument()
await expect
.element(screen.getByRole('button', { name: 'Screenshot' }))
.toBeInTheDocument()
await expect
.element(screen.getByRole('button', { name: 'Record' }))
.toBeInTheDocument()
await expect
.element(screen.getByRole('button', { name: 'Stop' }))
.toBeInTheDocument()
await expect
.element(screen.getByRole('button', { name: 'Submit' }))
.toBeInTheDocument()
await expect
.element(screen.getByRole('button', { name: 'Discard' }))
.toBeInTheDocument()
const closeButton = screen.getByText('✕')
await closeButton.click()
})
})
describe('Webcam Component', () => {
test('renders with title, control buttons, and close functionality works', async () => {
const screen = render(<App />)
await screen.getByRole('button', { name: 'Webcam' }).click()
await expect
.element(screen.getByRole('heading', { name: 'Camera' }))
.toBeInTheDocument()
await expect
.element(screen.getByRole('button', { name: 'Snapshot' }))
.toBeInTheDocument()
await expect
.element(screen.getByRole('button', { name: 'Record' }))
.toBeInTheDocument()
await expect
.element(screen.getByRole('button', { name: 'Stop' }))
.toBeInTheDocument()
await expect
.element(screen.getByRole('button', { name: 'Submit' }))
.toBeInTheDocument()
await expect
.element(screen.getByRole('button', { name: 'Discard' }))
.toBeInTheDocument()
const closeButton = screen.getByText('✕')
await closeButton.click()
})
})
describe('RemoteSource Component', () => {
test('renders login button and login interaction works', async () => {
const screen = render(<App />)
await screen.getByRole('button', { name: 'Dropbox' }).click()
const loginButton = screen.getByRole('button', { name: 'Login' })
await expect.element(loginButton).toBeInTheDocument()
await loginButton.click()
await expect.element(loginButton).toBeInTheDocument()
})
})