197 lines
5.2 KiB
TypeScript
197 lines
5.2 KiB
TypeScript
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',
|
||
};
|
||
}
|