import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
import { openDmsFilePreview, supportsImagePreview, supportsOnlyOfficePreview } from '../dmsFilePreview';
|
|
const mocks = vi.hoisted(() => ({
|
createTicket: vi.fn(),
|
imagePreview: vi.fn(),
|
openOfficeDocument: vi.fn(),
|
}));
|
|
vi.mock('@jnpf/hooks', () => ({
|
useGlobSetting: () => ({ apiURL: '/dev/' }),
|
}));
|
|
vi.mock('@jnpf/ui', () => ({
|
createImgPreview: mocks.imagePreview,
|
}));
|
|
vi.mock('#/api/x/dms/preview', () => ({
|
createDmsFilePreviewTicket: mocks.createTicket,
|
}));
|
|
vi.mock('#/utils/jnpf', () => ({
|
onlineUtils: { openOfficeDocument: mocks.openOfficeDocument },
|
}));
|
|
describe('dMS file preview', () => {
|
beforeEach(() => {
|
mocks.createTicket.mockReset();
|
mocks.imagePreview.mockReset();
|
mocks.openOfficeDocument.mockReset();
|
vi.stubGlobal('crypto', { randomUUID: () => '018f0000-0000-7000-8000-000000000703' });
|
});
|
|
afterEach(() => {
|
vi.useRealTimers();
|
vi.unstubAllGlobals();
|
});
|
|
it('rejects unsupported file extensions before requesting a ticket', async () => {
|
expect(supportsOnlyOfficePreview('archive.zip')).toBe(false);
|
expect(supportsImagePreview('archive.zip')).toBe(false);
|
|
await expect(openDmsFilePreview({ fileName: 'archive.zip', resourceId: 'version-1', resourceType: 'FILE_VERSION' })).rejects.toThrow(
|
'OnlyOffice 不支持该文件格式',
|
);
|
expect(mocks.createTicket).not.toHaveBeenCalled();
|
});
|
|
it('requests a ticket and opens an image with the JNPF image preview', async () => {
|
vi.useFakeTimers();
|
mocks.createTicket.mockResolvedValue({
|
data: {
|
accessTicket: 'ticket+/=',
|
expiresAt: '2026-08-24T12:05:00Z',
|
fileName: 'stored-image',
|
fileVersionId: 'version/1',
|
mimeType: 'image/png; charset=binary',
|
sizeBytes: 1024,
|
},
|
});
|
|
expect(supportsImagePreview('20-avatar-space.PNG')).toBe(true);
|
|
const options = { fileName: '20-avatar-space.PNG', resourceId: 'image-version-1', resourceType: 'FILE_VERSION' as const };
|
await expect(openDmsFilePreview(options)).resolves.toBe(true);
|
await expect(openDmsFilePreview(options)).resolves.toBe(false);
|
|
expect(mocks.createTicket).toHaveBeenCalledWith({
|
operationId: '018f0000-0000-7000-8000-000000000703',
|
resourceId: 'image-version-1',
|
resourceType: 'FILE_VERSION',
|
});
|
expect(mocks.imagePreview).toHaveBeenCalledWith({
|
imageList: ['/dev/api/file/Dms/OnlyOffice/version%2F1?ticket=ticket%2B%2F%3D'],
|
});
|
expect(mocks.openOfficeDocument).not.toHaveBeenCalled();
|
expect(mocks.createTicket).toHaveBeenCalledTimes(1);
|
|
await vi.advanceTimersByTimeAsync(500);
|
await expect(openDmsFilePreview(options)).resolves.toBe(true);
|
expect(mocks.createTicket).toHaveBeenCalledTimes(2);
|
await vi.advanceTimersByTimeAsync(500);
|
});
|
|
it('uses the list MIME type to admit an image whose name has no extension', async () => {
|
vi.useFakeTimers();
|
mocks.createTicket.mockResolvedValue({
|
data: {
|
accessTicket: 'ticket-1',
|
fileName: 'stored-image',
|
fileVersionId: 'version-1',
|
mimeType: 'image/webp',
|
},
|
});
|
|
await expect(
|
openDmsFilePreview({ fileName: 'stored-image', mimeType: 'image/webp', resourceId: 'image-version-2', resourceType: 'FILE_VERSION' }),
|
).resolves.toBe(true);
|
|
expect(mocks.imagePreview).toHaveBeenCalledOnce();
|
expect(mocks.openOfficeDocument).not.toHaveBeenCalled();
|
await vi.advanceTimersByTimeAsync(500);
|
});
|
|
it('falls back to getRandomValues when randomUUID is unavailable', async () => {
|
const getRandomValues = vi.fn((bytes: Uint8Array) => bytes.fill(0));
|
vi.stubGlobal('crypto', { getRandomValues });
|
mocks.createTicket.mockResolvedValue({
|
data: {
|
accessTicket: 'ticket-1',
|
fileName: 'quality-manual.docx',
|
fileVersionId: 'version-1',
|
},
|
});
|
|
await openDmsFilePreview({ fileName: 'quality-manual.docx', resourceId: 'version-1', resourceType: 'FILE_VERSION' });
|
|
expect(getRandomValues).toHaveBeenCalledOnce();
|
expect(mocks.createTicket).toHaveBeenCalledWith(expect.objectContaining({ operationId: '00000000-0000-4000-8000-000000000000' }));
|
});
|
|
it('requests a ticket and opens the global modal in DMS view mode', async () => {
|
mocks.createTicket.mockResolvedValue({
|
data: {
|
accessTicket: 'ticket-1',
|
expiresAt: '2026-08-24T12:05:00Z',
|
fileName: 'quality-manual.docx',
|
fileVersionId: 'version-1',
|
sizeBytes: 2048,
|
},
|
});
|
|
await openDmsFilePreview({
|
fileName: 'quality-manual.docx',
|
resourceId: 'document-1',
|
resourceType: 'DOCUMENT',
|
shifouShoukong: 'no',
|
} as any);
|
|
expect(mocks.createTicket).toHaveBeenCalledWith({
|
operationId: '018f0000-0000-7000-8000-000000000703',
|
resourceId: 'document-1',
|
resourceType: 'DOCUMENT',
|
});
|
expect(mocks.createTicket.mock.calls[0]![0]).not.toHaveProperty('shifouShoukong');
|
expect(mocks.openOfficeDocument).toHaveBeenCalledWith({
|
dmsAccessTicket: 'ticket-1',
|
dmsPreviewExpiresAt: '2026-08-24T12:05:00Z',
|
refreshDmsPreviewTicket: expect.any(Function),
|
file: {
|
fileId: 'version-1',
|
fileSize: 2048,
|
name: 'quality-manual.docx',
|
},
|
fileType: 'dms',
|
mode: 'view',
|
title: 'quality-manual.docx',
|
});
|
expect(mocks.imagePreview).not.toHaveBeenCalled();
|
});
|
|
it('preserves multiple business types when refreshing a preview ticket', async () => {
|
mocks.createTicket.mockResolvedValue({ data: {
|
accessTicket: 'ticket-1', expiresAt: '2026-08-24T12:05:00Z',
|
fileName: 'quality-manual.docx', fileVersionId: 'version-1',
|
} });
|
await openDmsFilePreview({
|
fileName: 'quality-manual.docx', resourceId: 'version-1', resourceType: 'FILE_VERSION',
|
watermarkBusinessTypes: ['RECORD', 'REPORT'],
|
});
|
const modal = mocks.openOfficeDocument.mock.calls[0]![0];
|
await modal.refreshDmsPreviewTicket();
|
expect(mocks.createTicket.mock.calls.map(([request]) => request.watermarkBusinessTypes)).toEqual([
|
['RECORD', 'REPORT'], ['RECORD', 'REPORT'],
|
]);
|
});
|
|
it('does not request a second ticket while the same resource is opening', async () => {
|
let resolveTicket: (value: unknown) => void = () => {};
|
mocks.createTicket.mockReturnValue(new Promise((resolve) => (resolveTicket = resolve)));
|
const options = { fileName: 'quality-manual.docx', resourceId: 'version-1', resourceType: 'FILE_VERSION' as const };
|
|
const first = openDmsFilePreview(options);
|
const second = openDmsFilePreview(options);
|
|
await expect(second).resolves.toBe(false);
|
expect(mocks.createTicket).toHaveBeenCalledTimes(1);
|
|
resolveTicket({
|
data: {
|
accessTicket: 'ticket-1',
|
expiresAt: '2026-08-24T12:05:00Z',
|
fileName: 'quality-manual.docx',
|
fileVersionId: 'version-1',
|
},
|
});
|
await first;
|
expect(mocks.openOfficeDocument).toHaveBeenCalledTimes(1);
|
});
|
|
it('keeps requests with distinct labels, pages or repeated entries separate while opening', async () => {
|
const ticket = { data: { accessTicket: 'ticket-1', fileName: 'quality.docx', fileVersionId: 'version-1' } };
|
mocks.createTicket.mockResolvedValue(ticket);
|
const resource = { fileName: 'quality.docx', resourceId: 'version-1', resourceType: 'FILE_VERSION' as const };
|
const entry = { page: 4, barValue: 'REC-004', barText: 'Page four' };
|
const arrays = [[entry], [{ ...entry, barText: 'Different text' }], [{ ...entry, page: 5 }], [entry, entry]];
|
await Promise.all(arrays.map((customBarcode) => openDmsFilePreview({ ...resource, customBarcode })));
|
expect(mocks.createTicket).toHaveBeenCalledTimes(4);
|
arrays.forEach((customBarcode, index) => {
|
expect(mocks.createTicket).toHaveBeenNthCalledWith(index + 1, expect.objectContaining({ customBarcode }));
|
});
|
expect(mocks.openOfficeDocument).toHaveBeenCalledTimes(4);
|
});
|
});
|