694 lines
19 KiB
TypeScript
694 lines
19 KiB
TypeScript
import { act, fireEvent, render, screen } from '@testing-library/react'
|
|
import { MemoryRouter } from 'react-router-dom'
|
|
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
|
|
|
|
import type { LocalTransferRecord } from '@/lib/indexeddb/db'
|
|
|
|
const { toDataUrlMock } = vi.hoisted(() => ({
|
|
toDataUrlMock: vi.fn(async () => 'data:image/png;base64,qr'),
|
|
}))
|
|
|
|
vi.mock('qrcode', () => ({
|
|
default: {
|
|
toDataURL: toDataUrlMock,
|
|
},
|
|
}))
|
|
|
|
import { ShareCard } from './ShareCard'
|
|
|
|
function createTransfer(
|
|
id: string,
|
|
displayName: string,
|
|
expiresAt: string,
|
|
overrides: Partial<LocalTransferRecord> = {},
|
|
): LocalTransferRecord {
|
|
return {
|
|
clearLocalSecretsOnReady: false,
|
|
createdAt: '2026-03-20T08:00:00.000Z',
|
|
displayName,
|
|
expiresAt,
|
|
files: [],
|
|
id,
|
|
linkKeyBase64Url: 'AQID',
|
|
localManagementCleared: false,
|
|
manageToken: 'manage-token',
|
|
metadataStrippingEnabled: false,
|
|
rootKeyBase64Url: 'AQID',
|
|
shareUrl: `https://example.com/t/${id}#k=test`,
|
|
sourcePersisted: false,
|
|
status: 'ready',
|
|
totalBytes: 2048,
|
|
totalFiles: 2,
|
|
uploadedBytes: 2048,
|
|
...overrides,
|
|
}
|
|
}
|
|
|
|
describe('ShareCard', () => {
|
|
const extendTransferMock = vi.fn(async () => {})
|
|
|
|
beforeEach(() => {
|
|
vi.useFakeTimers()
|
|
vi.setSystemTime(new Date('2026-03-20T10:00:00.000Z'))
|
|
toDataUrlMock.mockClear()
|
|
extendTransferMock.mockClear()
|
|
Object.assign(navigator, {
|
|
clipboard: {
|
|
writeText: vi.fn(async () => {}),
|
|
},
|
|
share: undefined,
|
|
})
|
|
})
|
|
|
|
afterEach(() => {
|
|
vi.useRealTimers()
|
|
})
|
|
|
|
it('keeps the normal share actions for active transfers', async () => {
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-1', 'Active transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(screen.getByRole('heading', { name: /Share this transfer/i })).toBeInTheDocument()
|
|
expect(screen.getByRole('button', { name: /Copy link/i })).toBeInTheDocument()
|
|
expect(screen.getByRole('button', { name: /Share link/i })).toBeInTheDocument()
|
|
expect(
|
|
screen.getByRole('button', { name: /Set expiry to 1 week from now/i }),
|
|
).toBeInTheDocument()
|
|
expect(
|
|
screen.queryByDisplayValue('https://example.com/t/active-1#k=test'),
|
|
).not.toBeInTheDocument()
|
|
expect(screen.getByAltText('Transfer QR code')).toBeInTheDocument()
|
|
expect(toDataUrlMock).toHaveBeenCalledTimes(1)
|
|
})
|
|
|
|
it('switches expired transfers into an archive-style state', () => {
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('expired-1', 'Expired transfer', '2026-03-19T10:00:00.000Z')}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
expect(screen.getByRole('heading', { name: /This transfer expired/i })).toBeInTheDocument()
|
|
expect(screen.getByText('Expired transfer')).toBeInTheDocument()
|
|
expect(screen.getByText(/Recipients opening the saved/i)).toBeInTheDocument()
|
|
expect(screen.queryByRole('button', { name: /Copy link/i })).not.toBeInTheDocument()
|
|
expect(screen.queryByRole('button', { name: /Share link/i })).not.toBeInTheDocument()
|
|
expect(screen.queryByAltText('Transfer QR code')).not.toBeInTheDocument()
|
|
expect(screen.getByRole('link', { name: /Manage transfers/i })).toBeInTheDocument()
|
|
expect(toDataUrlMock).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('renders a missing-transfer state when no local record exists', () => {
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard transfer={undefined} />
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
expect(screen.getByText('Transfer not on this device')).toBeInTheDocument()
|
|
expect(
|
|
screen.getByText('Open it in the same browser on the device that created it.'),
|
|
).toBeInTheDocument()
|
|
})
|
|
|
|
it('copies the full link and resets the copied state', async () => {
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-2', 'Copied transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Copy link' }))
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
|
|
'https://example.com/t/active-2#k=test',
|
|
)
|
|
expect(screen.getByRole('button', { name: 'Copied' })).toBeInTheDocument()
|
|
|
|
await act(async () => {
|
|
vi.advanceTimersByTime(1800)
|
|
})
|
|
|
|
expect(screen.getByRole('button', { name: 'Copy link' })).toBeInTheDocument()
|
|
})
|
|
|
|
it('uses the platform share sheet when available', async () => {
|
|
const shareMock = vi.fn(async () => {})
|
|
Object.assign(navigator, { share: shareMock })
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-3', 'Shared transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Share link' }))
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(shareMock).toHaveBeenCalledWith({
|
|
text: 'Encrypted files via Xdrop',
|
|
title: 'Shared transfer',
|
|
url: 'https://example.com/t/active-3#k=test',
|
|
})
|
|
expect(navigator.clipboard.writeText).not.toHaveBeenCalled()
|
|
})
|
|
|
|
it('omits the share title when the transfer has no display name', async () => {
|
|
const shareMock = vi.fn(async () => {})
|
|
Object.assign(navigator, { share: shareMock })
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-3b', '', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Share link' }))
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(shareMock).toHaveBeenCalledWith({
|
|
text: 'Encrypted files via Xdrop',
|
|
url: 'https://example.com/t/active-3b#k=test',
|
|
})
|
|
})
|
|
|
|
it('falls back to copying when the share sheet is unavailable', async () => {
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-4', 'Fallback transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Share link' }))
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
|
|
'https://example.com/t/active-4#k=test',
|
|
)
|
|
expect(screen.getByRole('button', { name: 'Copied' })).toBeInTheDocument()
|
|
})
|
|
|
|
it('falls back to copying when the share sheet fails for non-abort reasons', async () => {
|
|
const shareMock = vi.fn(async () => {
|
|
throw new Error('share failed')
|
|
})
|
|
Object.assign(navigator, { share: shareMock })
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-4b', 'Fallback transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Share link' }))
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(shareMock).toHaveBeenCalledTimes(1)
|
|
expect(navigator.clipboard.writeText).toHaveBeenCalledWith(
|
|
'https://example.com/t/active-4b#k=test',
|
|
)
|
|
})
|
|
|
|
it('shows a retry-focused fallback when clipboard access fails', async () => {
|
|
Object.assign(navigator, {
|
|
clipboard: {
|
|
writeText: vi.fn(async () => {
|
|
throw new Error('denied')
|
|
}),
|
|
},
|
|
})
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-6', 'Clipboard fallback', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Copy link' }))
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(
|
|
screen.getByText(/Use your browser share tools or try again on this device/i),
|
|
).toBeInTheDocument()
|
|
expect(
|
|
screen.queryByDisplayValue('https://example.com/t/active-6#k=test'),
|
|
).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('ignores canceled share-sheet requests without surfacing an error', async () => {
|
|
const shareMock = vi.fn(async () => {
|
|
throw new DOMException('dismissed', 'AbortError')
|
|
})
|
|
Object.assign(navigator, { share: shareMock })
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-7', 'Dismissed share', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: 'Share link' }))
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(shareMock).toHaveBeenCalledTimes(1)
|
|
expect(screen.queryByText(/copy failed/i)).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('renders sender guidance for paused, failed, deleted, and uploading transfers', async () => {
|
|
const pausedTransfer = createTransfer(
|
|
'paused-1',
|
|
'Paused transfer',
|
|
'2026-03-21T10:00:00.000Z',
|
|
{
|
|
localManagementCleared: true,
|
|
status: 'paused',
|
|
totalBytes: 4000,
|
|
uploadedBytes: 1000,
|
|
},
|
|
)
|
|
const failedTransfer = createTransfer(
|
|
'failed-1',
|
|
'Failed transfer',
|
|
'2026-03-21T10:00:00.000Z',
|
|
{
|
|
status: 'failed',
|
|
totalBytes: 4000,
|
|
uploadedBytes: 1000,
|
|
},
|
|
)
|
|
const deletedTransfer = createTransfer(
|
|
'deleted-1',
|
|
'Deleted transfer',
|
|
'2026-03-21T10:00:00.000Z',
|
|
{
|
|
status: 'deleted',
|
|
},
|
|
)
|
|
const uploadingTransfer = createTransfer(
|
|
'uploading-1',
|
|
'Uploading transfer',
|
|
'2026-03-21T10:00:00.000Z',
|
|
{
|
|
status: 'uploading',
|
|
totalBytes: 4000,
|
|
uploadedBytes: 1000,
|
|
},
|
|
)
|
|
|
|
const { rerender } = render(
|
|
<MemoryRouter>
|
|
<ShareCard transfer={pausedTransfer} onExtendTransfer={extendTransferMock} />
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(screen.getByText('25% uploaded')).toBeInTheDocument()
|
|
expect(
|
|
screen.getByText(
|
|
'Upload will continue automatically when you return here in the same browser on this device.',
|
|
),
|
|
).toBeInTheDocument()
|
|
expect(screen.getByText(/Privacy mode removed local transfer controls/i)).toBeInTheDocument()
|
|
|
|
rerender(
|
|
<MemoryRouter>
|
|
<ShareCard transfer={failedTransfer} onExtendTransfer={extendTransferMock} />
|
|
</MemoryRouter>,
|
|
)
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
expect(screen.getByText('Upload stopped in this browser on this device.')).toBeInTheDocument()
|
|
|
|
rerender(
|
|
<MemoryRouter>
|
|
<ShareCard transfer={deletedTransfer} onExtendTransfer={extendTransferMock} />
|
|
</MemoryRouter>,
|
|
)
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
expect(screen.getByText('This transfer was deleted.')).toBeInTheDocument()
|
|
expect(screen.queryByText(/uploaded/i)).not.toBeInTheDocument()
|
|
|
|
rerender(
|
|
<MemoryRouter>
|
|
<ShareCard transfer={uploadingTransfer} onExtendTransfer={extendTransferMock} />
|
|
</MemoryRouter>,
|
|
)
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
expect(screen.getByText('The link will work once the upload finishes.')).toBeInTheDocument()
|
|
})
|
|
|
|
it('extends expiry from the share page and surfaces failures locally', async () => {
|
|
extendTransferMock.mockRejectedValueOnce(new Error('Extend failed'))
|
|
|
|
const transfer = createTransfer('active-8', 'Extend transfer', '2026-03-21T10:00:00.000Z')
|
|
const { rerender } = render(
|
|
<MemoryRouter>
|
|
<ShareCard transfer={transfer} onExtendTransfer={extendTransferMock} />
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: /Set expiry to 1 week from now/i }))
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(extendTransferMock).toHaveBeenCalledWith('active-8', 7 * 24 * 60 * 60)
|
|
expect(screen.getByText('Extend failed')).toBeInTheDocument()
|
|
|
|
extendTransferMock.mockResolvedValueOnce(undefined)
|
|
rerender(
|
|
<MemoryRouter>
|
|
<ShareCard transfer={transfer} onExtendTransfer={extendTransferMock} />
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: /Set expiry to 1 week from now/i }))
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(screen.queryByText('Extend failed')).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('falls back to a generic message when extending fails with a non-Error', async () => {
|
|
extendTransferMock.mockRejectedValueOnce('boom')
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-8b', 'Extend transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
fireEvent.click(screen.getByRole('button', { name: /Set expiry to 1 week from now/i }))
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(screen.getByText('Could not update this transfer right now.')).toBeInTheDocument()
|
|
})
|
|
|
|
it('hides the extend action when local management is unavailable', async () => {
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-9', 'No manage token', '2026-03-21T10:00:00.000Z', {
|
|
manageToken: '',
|
|
})}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(
|
|
screen.queryByRole('button', { name: /Set expiry to 1 week from now/i }),
|
|
).not.toBeInTheDocument()
|
|
})
|
|
|
|
it('guards copy and share handlers when a transfer has no share link', async () => {
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-10', 'Missing share link', '2026-03-21T10:00:00.000Z', {
|
|
shareUrl: '',
|
|
})}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
const copyButton = screen.getByRole('button', { name: 'Copy link' }) as HTMLButtonElement
|
|
const shareButton = screen.getByRole('button', { name: 'Share link' }) as HTMLButtonElement
|
|
|
|
expect(copyButton).toBeDisabled()
|
|
expect(shareButton).toBeDisabled()
|
|
|
|
copyButton.disabled = false
|
|
shareButton.disabled = false
|
|
|
|
fireEvent.click(copyButton)
|
|
fireEvent.click(shareButton)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(navigator.clipboard.writeText).not.toHaveBeenCalled()
|
|
expect(toDataUrlMock).not.toHaveBeenCalled()
|
|
expect(document.querySelector('.qr-placeholder')).not.toBeNull()
|
|
})
|
|
|
|
it('keeps the QR placeholder when QR generation fails', async () => {
|
|
toDataUrlMock.mockRejectedValueOnce(new Error('QR failed'))
|
|
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-5', 'Broken QR transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(screen.queryByAltText('Transfer QR code')).not.toBeInTheDocument()
|
|
expect(document.querySelector('.qr-placeholder')).not.toBeNull()
|
|
})
|
|
|
|
it('clears a stale QR image when regenerating the same link fails later', async () => {
|
|
toDataUrlMock
|
|
.mockResolvedValueOnce('data:image/png;base64,first')
|
|
.mockRejectedValueOnce(new Error('QR failed'))
|
|
|
|
const { rerender } = render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-5b', 'QR reset transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(screen.getByAltText('Transfer QR code')).toHaveAttribute(
|
|
'src',
|
|
'data:image/png;base64,first',
|
|
)
|
|
|
|
rerender(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-5b', 'QR reset transfer', '2026-03-19T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
rerender(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-5b', 'QR reset transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(screen.queryByAltText('Transfer QR code')).not.toBeInTheDocument()
|
|
expect(document.querySelector('.qr-placeholder')).not.toBeNull()
|
|
})
|
|
|
|
it('shows the expired transfer error when one was saved locally', () => {
|
|
render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('expired-2', 'Expired transfer', '2026-03-19T10:00:00.000Z', {
|
|
lastError: 'Upload stalled before expiry.',
|
|
})}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
expect(screen.getByText('Upload stalled before expiry.')).toBeInTheDocument()
|
|
})
|
|
|
|
it('ignores stale QR responses after the transfer changes', async () => {
|
|
let resolveFirstQr: ((value: string) => void) | undefined
|
|
let resolveSecondQr: ((value: string) => void) | undefined
|
|
toDataUrlMock
|
|
.mockImplementationOnce(
|
|
() =>
|
|
new Promise((resolve) => {
|
|
resolveFirstQr = resolve
|
|
}),
|
|
)
|
|
.mockImplementationOnce(
|
|
() =>
|
|
new Promise((resolve) => {
|
|
resolveSecondQr = resolve
|
|
}),
|
|
)
|
|
|
|
const { rerender } = render(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-old', 'Old transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
rerender(
|
|
<MemoryRouter>
|
|
<ShareCard
|
|
transfer={createTransfer('active-new', 'New transfer', '2026-03-21T10:00:00.000Z')}
|
|
onExtendTransfer={extendTransferMock}
|
|
/>
|
|
</MemoryRouter>,
|
|
)
|
|
|
|
await act(async () => {
|
|
resolveFirstQr?.('data:image/png;base64,old')
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(screen.queryByAltText('Transfer QR code')).not.toBeInTheDocument()
|
|
expect(document.querySelector('.qr-placeholder')).not.toBeNull()
|
|
|
|
await act(async () => {
|
|
resolveSecondQr?.('data:image/png;base64,new')
|
|
await Promise.resolve()
|
|
})
|
|
|
|
expect(screen.getByAltText('Transfer QR code')).toHaveAttribute(
|
|
'src',
|
|
'data:image/png;base64,new',
|
|
)
|
|
})
|
|
})
|