chore: reset repository history
This commit is contained in:
commit
b451771169
172 files changed
+26281
No files matched your search
@@ -0,0 +1,239 @@
|
||||
import { act, fireEvent, render, screen, within } 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 { deleteTransferMock, extendTransferMock, transfersState } = vi.hoisted(() => ({
|
||||
transfersState: [] as LocalTransferRecord[],
|
||||
deleteTransferMock: vi.fn(async () => {}),
|
||||
extendTransferMock: vi.fn(async () => {}),
|
||||
}))
|
||||
|
||||
vi.mock('@/features/upload/TransferContext', () => ({
|
||||
useTransfers: () => ({
|
||||
transfers: transfersState,
|
||||
deleteTransfer: deleteTransferMock,
|
||||
extendTransfer: extendTransferMock,
|
||||
}),
|
||||
}))
|
||||
|
||||
import { HistoryBoard } from './HistoryBoard'
|
||||
|
||||
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}`,
|
||||
sourcePersisted: false,
|
||||
status: 'ready',
|
||||
totalBytes: 2048,
|
||||
totalFiles: 2,
|
||||
uploadedBytes: 2048,
|
||||
...overrides,
|
||||
}
|
||||
}
|
||||
|
||||
describe('HistoryBoard', () => {
|
||||
beforeEach(() => {
|
||||
vi.useFakeTimers()
|
||||
vi.setSystemTime(new Date('2026-03-20T10:00:00.000Z'))
|
||||
transfersState.splice(0, transfersState.length)
|
||||
deleteTransferMock.mockClear()
|
||||
extendTransferMock.mockClear()
|
||||
Object.assign(navigator, {
|
||||
clipboard: {
|
||||
writeText: vi.fn(async () => {}),
|
||||
},
|
||||
})
|
||||
})
|
||||
|
||||
afterEach(() => {
|
||||
vi.useRealTimers()
|
||||
})
|
||||
|
||||
function renderBoard() {
|
||||
return render(
|
||||
<MemoryRouter>
|
||||
<HistoryBoard />
|
||||
</MemoryRouter>,
|
||||
)
|
||||
}
|
||||
|
||||
it('collapses expired transfers by default and expands them on demand', async () => {
|
||||
transfersState.splice(
|
||||
0,
|
||||
transfersState.length,
|
||||
createTransfer('active-1', 'Active transfer', '2026-03-21T10:00:00.000Z'),
|
||||
createTransfer('expired-1', 'Expired transfer', '2026-03-19T10:00:00.000Z'),
|
||||
)
|
||||
|
||||
renderBoard()
|
||||
|
||||
expect(screen.getByText('Active transfer')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: /Expired transfers/i })).toBeInTheDocument()
|
||||
expect(screen.queryByText('Expired transfer')).not.toBeInTheDocument()
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /Expired transfers/i }))
|
||||
|
||||
const expiredCard = screen.getByText('Expired transfer').closest('.history-transfer-card')
|
||||
|
||||
expect(expiredCard).not.toBeNull()
|
||||
expect(screen.getByText('Expired transfer')).toBeInTheDocument()
|
||||
expect(
|
||||
within(expiredCard as HTMLElement).queryByRole('button', { name: /Copy link/i }),
|
||||
).not.toBeInTheDocument()
|
||||
expect(screen.queryByRole('button', { name: /Restore for 1 week/i })).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('does not show the empty state when only expired transfers exist', () => {
|
||||
transfersState.splice(
|
||||
0,
|
||||
transfersState.length,
|
||||
createTransfer('expired-1', 'Expired transfer', '2026-03-19T10:00:00.000Z'),
|
||||
)
|
||||
|
||||
renderBoard()
|
||||
|
||||
expect(screen.queryByText('No transfers on this device')).not.toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('shows the empty state when no transfers exist', () => {
|
||||
renderBoard()
|
||||
|
||||
expect(screen.getByText('No transfers on this device')).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('copies links, shows progress, and renders local warnings for active transfers', async () => {
|
||||
transfersState.splice(
|
||||
0,
|
||||
transfersState.length,
|
||||
createTransfer('uploading-1', 'Uploading transfer', '2026-03-21T10:00:00.000Z', {
|
||||
lastError: 'Upload paused by network',
|
||||
localManagementCleared: true,
|
||||
status: 'uploading',
|
||||
totalBytes: 4000,
|
||||
uploadedBytes: 1000,
|
||||
}),
|
||||
createTransfer('local-only-1', 'Local only transfer', '2026-03-21T10:00:00.000Z', {
|
||||
manageToken: '',
|
||||
}),
|
||||
)
|
||||
|
||||
renderBoard()
|
||||
|
||||
expect(screen.getByText('25% uploaded')).toBeInTheDocument()
|
||||
expect(screen.getByText(/Privacy mode removed local transfer controls/i)).toBeInTheDocument()
|
||||
expect(screen.getByText('Upload paused by network')).toBeInTheDocument()
|
||||
expect(screen.getByRole('button', { name: 'Forget local copy' })).toBeInTheDocument()
|
||||
|
||||
const uploadingCard = screen.getByText('Uploading transfer').closest('.card')
|
||||
expect(uploadingCard).not.toBeNull()
|
||||
expect(
|
||||
within(uploadingCard as HTMLElement).getByRole('link', { name: 'Open share page' }),
|
||||
).toHaveAttribute('href', '/share/uploading-1')
|
||||
|
||||
fireEvent.click(within(uploadingCard as HTMLElement).getByRole('button', { name: 'Copy link' }))
|
||||
|
||||
await act(async () => {
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
expect(navigator.clipboard.writeText).toHaveBeenCalledWith('https://example.com/t/uploading-1')
|
||||
expect(
|
||||
within(uploadingCard as HTMLElement).getByRole('button', { name: 'Copied' }),
|
||||
).toBeInTheDocument()
|
||||
|
||||
await act(async () => {
|
||||
vi.advanceTimersByTime(1800)
|
||||
})
|
||||
|
||||
expect(
|
||||
within(uploadingCard as HTMLElement).getByRole('button', { name: 'Copy link' }),
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
|
||||
it('surfaces async action failures and clears stale errors before retrying', async () => {
|
||||
transfersState.splice(
|
||||
0,
|
||||
transfersState.length,
|
||||
createTransfer('ready-1', 'Ready transfer', '2026-03-21T10:00:00.000Z'),
|
||||
)
|
||||
extendTransferMock.mockRejectedValueOnce(new Error('Extend failed'))
|
||||
deleteTransferMock.mockRejectedValueOnce('unexpected')
|
||||
|
||||
renderBoard()
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: /Set expiry to 1 week from now/i }))
|
||||
|
||||
await act(async () => {
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
expect(screen.getByText('Extend failed')).toBeInTheDocument()
|
||||
expect(extendTransferMock).toHaveBeenCalledWith('ready-1', expect.any(Number))
|
||||
|
||||
extendTransferMock.mockResolvedValueOnce(undefined)
|
||||
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()
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Delete' }))
|
||||
expect(
|
||||
screen.getByText(/Confirm delete to remove this transfer from storage and from this device/i),
|
||||
).toBeInTheDocument()
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Confirm delete' }))
|
||||
|
||||
await act(async () => {
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
expect(screen.getByText('Could not update this transfer right now.')).toBeInTheDocument()
|
||||
expect(deleteTransferMock).toHaveBeenCalledWith('ready-1')
|
||||
})
|
||||
|
||||
it('shows a scoped fallback when clipboard access is unavailable', async () => {
|
||||
Object.assign(navigator, {
|
||||
clipboard: {
|
||||
writeText: vi.fn(async () => {
|
||||
throw new Error('denied')
|
||||
}),
|
||||
},
|
||||
})
|
||||
transfersState.splice(
|
||||
0,
|
||||
transfersState.length,
|
||||
createTransfer('ready-2', 'Ready transfer', '2026-03-21T10:00:00.000Z'),
|
||||
)
|
||||
|
||||
renderBoard()
|
||||
|
||||
fireEvent.click(screen.getByRole('button', { name: 'Copy link' }))
|
||||
|
||||
await act(async () => {
|
||||
await Promise.resolve()
|
||||
})
|
||||
|
||||
expect(
|
||||
screen.getByText(/Open the share page to copy the full link manually/i),
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
})
|
||||
Reference in new issue
Block a user