feat: publish av-tools 0.1.0
This commit is contained in:
196
tests/unit/components/job-panel.test.tsx
Normal file
196
tests/unit/components/job-panel.test.tsx
Normal file
@@ -0,0 +1,196 @@
|
||||
import { cleanup, render, screen, waitFor } from '@testing-library/react';
|
||||
import userEvent from '@testing-library/user-event';
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
import { JobPanel } from '../../../src/components/JobPanel';
|
||||
import type { ExportResult } from '../../../src/export';
|
||||
|
||||
afterEach(() => {
|
||||
cleanup();
|
||||
vi.restoreAllMocks();
|
||||
});
|
||||
|
||||
describe('JobPanel result selection', () => {
|
||||
it('downloads only the selected generated results as ZIP', async () => {
|
||||
const user = userEvent.setup();
|
||||
const onSaveZip = vi.fn();
|
||||
const first = result('one', 'one.png');
|
||||
const second = result('two', 'two.png');
|
||||
|
||||
render(
|
||||
<JobPanel
|
||||
engineState={{
|
||||
status: 'ready',
|
||||
mode: 'single-thread',
|
||||
capabilities: {
|
||||
versionText: 'test',
|
||||
buildConfiguration: [],
|
||||
demuxers: new Set(),
|
||||
muxers: new Set(),
|
||||
decoders: new Set(),
|
||||
encoders: new Set(),
|
||||
filters: new Set(),
|
||||
},
|
||||
}}
|
||||
jobs={[]}
|
||||
results={[first, second]}
|
||||
verifications={{}}
|
||||
logs={[]}
|
||||
onCancel={vi.fn()}
|
||||
onCancelJob={vi.fn()}
|
||||
onRetryJob={vi.fn()}
|
||||
onClearJobHistory={vi.fn()}
|
||||
onSave={vi.fn()}
|
||||
onSaveZip={onSaveZip}
|
||||
onSaveReport={vi.fn()}
|
||||
onPreview={vi.fn()}
|
||||
onRemove={vi.fn()}
|
||||
onClearResults={vi.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
await waitFor(() =>
|
||||
expect(
|
||||
screen.getByRole('button', {
|
||||
name: 'Download selected as ZIP (2)',
|
||||
})
|
||||
).toBeEnabled()
|
||||
);
|
||||
await user.click(
|
||||
screen.getByRole('checkbox', { name: 'Select two.png for ZIP' })
|
||||
);
|
||||
await user.click(
|
||||
screen.getByRole('button', { name: 'Download selected as ZIP (1)' })
|
||||
);
|
||||
expect(onSaveZip).toHaveBeenCalledWith([first]);
|
||||
});
|
||||
|
||||
it('marks browser-rejected media as download-only and does not offer Preview', () => {
|
||||
vi.spyOn(HTMLMediaElement.prototype, 'canPlayType').mockReturnValue('');
|
||||
const output = mediaResult('audio', 'archive.flac', 'audio/flac');
|
||||
|
||||
render(
|
||||
<JobPanel
|
||||
engineState={{
|
||||
status: 'ready',
|
||||
mode: 'single-thread',
|
||||
capabilities: {
|
||||
versionText: 'test',
|
||||
buildConfiguration: [],
|
||||
demuxers: new Set(),
|
||||
muxers: new Set(),
|
||||
decoders: new Set(),
|
||||
encoders: new Set(),
|
||||
filters: new Set(),
|
||||
},
|
||||
}}
|
||||
jobs={[]}
|
||||
results={[output]}
|
||||
verifications={{}}
|
||||
logs={[]}
|
||||
onCancel={vi.fn()}
|
||||
onCancelJob={vi.fn()}
|
||||
onRetryJob={vi.fn()}
|
||||
onClearJobHistory={vi.fn()}
|
||||
onSave={vi.fn()}
|
||||
onSaveZip={vi.fn()}
|
||||
onSaveReport={vi.fn()}
|
||||
onPreview={vi.fn()}
|
||||
onRemove={vi.fn()}
|
||||
onClearResults={vi.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(screen.queryByRole('button', { name: 'Preview' })).toBeNull();
|
||||
expect(
|
||||
screen.getByText(/Download only: The browser reports no native/iu)
|
||||
).toBeVisible();
|
||||
expect(screen.getByRole('button', { name: 'Save result' })).toBeEnabled();
|
||||
});
|
||||
|
||||
it('shows the exact split interval and per-result verification state', () => {
|
||||
const output = {
|
||||
...mediaResult('segment', 'clip-001.mp4', 'video/mp4'),
|
||||
timeRange: { startSeconds: 1.25, endSeconds: 3.5 },
|
||||
};
|
||||
|
||||
render(
|
||||
<JobPanel
|
||||
engineState={{
|
||||
status: 'ready',
|
||||
mode: 'single-thread',
|
||||
capabilities: {
|
||||
versionText: 'test',
|
||||
buildConfiguration: [],
|
||||
demuxers: new Set(),
|
||||
muxers: new Set(),
|
||||
decoders: new Set(),
|
||||
encoders: new Set(),
|
||||
filters: new Set(),
|
||||
},
|
||||
}}
|
||||
jobs={[]}
|
||||
results={[output]}
|
||||
verifications={{
|
||||
segment: {
|
||||
warning: 'Output was created, but its verification probe failed.',
|
||||
},
|
||||
}}
|
||||
logs={[]}
|
||||
onCancel={vi.fn()}
|
||||
onCancelJob={vi.fn()}
|
||||
onRetryJob={vi.fn()}
|
||||
onClearJobHistory={vi.fn()}
|
||||
onSave={vi.fn()}
|
||||
onSaveZip={vi.fn()}
|
||||
onSaveReport={vi.fn()}
|
||||
onPreview={vi.fn()}
|
||||
onRemove={vi.fn()}
|
||||
onClearResults={vi.fn()}
|
||||
/>
|
||||
);
|
||||
|
||||
expect(
|
||||
screen.getByText(
|
||||
'Segment range 00:00:01.250–00:00:03.500 · 00:00:02.250 duration'
|
||||
)
|
||||
).toBeVisible();
|
||||
expect(screen.getByText(/created · verification warning/iu)).toBeVisible();
|
||||
expect(
|
||||
screen.getByText('Output was created, but its verification probe failed.')
|
||||
).toBeVisible();
|
||||
});
|
||||
});
|
||||
|
||||
function result(id: string, fileName: string): ExportResult {
|
||||
const blob = new Blob([id], { type: 'image/png' });
|
||||
return {
|
||||
id,
|
||||
planId: `plan-${id}`,
|
||||
outputId: `output-${id}`,
|
||||
fileName,
|
||||
blob,
|
||||
mimeType: blob.type,
|
||||
role: 'thumbnail',
|
||||
size: blob.size,
|
||||
createdAt: '2026-01-01T00:00:00.000Z',
|
||||
};
|
||||
}
|
||||
|
||||
function mediaResult(
|
||||
id: string,
|
||||
fileName: string,
|
||||
mimeType: string
|
||||
): ExportResult {
|
||||
const blob = new Blob([id], { type: mimeType });
|
||||
return {
|
||||
id,
|
||||
planId: `plan-${id}`,
|
||||
outputId: `output-${id}`,
|
||||
fileName,
|
||||
blob,
|
||||
mimeType,
|
||||
role: 'media',
|
||||
size: blob.size,
|
||||
createdAt: '2026-01-01T00:00:00.000Z',
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user