import React from 'react';
import { render, screen, fireEvent } from '@testing-library/react';
import '@testing-library/jest-dom';
import { MediaPreviewModal } from './MediaPreviewModal';
import type { Medium } from '@memori.ai/memori-api-client/dist/types';
beforeAll(() => {
if (typeof window !== 'undefined' && !window.IntersectionObserver) {
(window as unknown as { IntersectionObserver: unknown }).IntersectionObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
}));
}
if (typeof global !== 'undefined' && !globalThis.IntersectionObserver) {
(globalThis as unknown as { IntersectionObserver: unknown }).IntersectionObserver = jest.fn().mockImplementation(() => ({
observe: jest.fn(),
unobserve: jest.fn(),
disconnect: jest.fn(),
}));
}
});
jest.mock('../../helpers/media', () => ({
getResourceUrl: jest.fn(({ resourceURI }: { resourceURI?: string }) =>
resourceURI ? `https://resolved.example.com/${resourceURI}` : ''
),
}));
describe('MediaPreviewModal', () => {
const onClose = jest.fn();
beforeEach(() => {
jest.clearAllMocks();
});
describe('images', () => {
it('renders image in ContentPreviewModal with title and src', () => {
const medium: Medium = {
mediumID: 'img-1',
mimeType: 'image/jpeg',
title: 'Photo',
url: 'https://example.com/photo.jpg',
};
render(
);
expect(screen.getByAltText('Photo')).toBeInTheDocument();
expect(screen.getByAltText('Photo')).toHaveAttribute('src', expect.stringContaining('photo.jpg'));
});
it('uses base64 data URL when image has content', () => {
const medium: Medium = {
mediumID: 'img-2',
mimeType: 'image/png',
title: 'Inline',
content: 'base64content',
};
render(
);
const img = screen.getByAltText('Inline');
expect(img).toHaveAttribute('src', expect.stringMatching(/^data:image\/png;base64,/));
});
});
describe('code', () => {
it('renders code snippet with Snippet component', () => {
const medium: Medium = {
mediumID: 'code-1',
mimeType: 'text/javascript',
title: 'Script',
content: 'console.log("hello");',
};
render(
);
expect(screen.getByText(/console\.log/)).toBeInTheDocument();
});
});
describe('PDF', () => {
it('renders PDF in iframe when url or content present', () => {
const medium: Medium = {
mediumID: 'pdf-1',
mimeType: 'application/pdf',
title: 'Document',
url: 'https://example.com/doc.pdf',
};
render(
);
const iframe = screen.getByTitle('Document');
expect(iframe).toBeInTheDocument();
expect(iframe.tagName).toBe('IFRAME');
});
});
describe('Excel', () => {
it('renders Excel in iframe when url or content present', () => {
const medium: Medium = {
mediumID: 'xls-1',
mimeType: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
title: 'Sheet',
url: 'https://example.com/sheet.xlsx',
};
render(
);
const iframe = screen.getByTitle('Sheet');
expect(iframe).toBeInTheDocument();
});
});
describe('HTML', () => {
it('renders HTML link in iframe when only url', () => {
const medium: Medium = {
mediumID: 'html-1',
mimeType: 'text/html',
title: 'Page',
url: 'example.com/page',
};
render(
);
const iframe = screen.getByTitle('Page');
expect(iframe).toBeInTheDocument();
expect(iframe).toHaveAttribute('src', 'https://example.com/page');
});
it('renders HTML with content as stripped text in Snippet', () => {
const medium: Medium = {
mediumID: 'html-2',
mimeType: 'text/html',
title: 'Inline HTML',
content: '
Hello world
',
};
render(
);
expect(screen.getByText(/Hello world/)).toBeInTheDocument();
});
});
describe('video and audio', () => {
it('renders video with native controls', () => {
const medium: Medium = {
mediumID: 'vid-1',
mimeType: 'video/mp4',
title: 'Clip',
url: 'https://example.com/video.mp4',
};
render(
);
const video = document.querySelector('video');
expect(video).toBeInTheDocument();
expect(video).toHaveAttribute('src', expect.stringContaining('video.mp4'));
});
it('renders audio with title and controls', () => {
const medium: Medium = {
mediumID: 'aud-1',
mimeType: 'audio/mpeg',
title: 'Track',
url: 'https://example.com/audio.mp3',
};
render(
);
expect(screen.getAllByText('Track').length).toBeGreaterThan(0);
const audio = document.querySelector('audio');
expect(audio).toBeInTheDocument();
});
});
describe('plain text and markdown', () => {
it('renders plain text in Snippet', () => {
const medium: Medium = {
mediumID: 'txt-1',
mimeType: 'text/plain',
title: 'Notes',
content: 'Some plain text.',
};
render(
);
expect(screen.getByText(/Some plain text/)).toBeInTheDocument();
});
it('renders markdown in Snippet', () => {
const medium: Medium = {
mediumID: 'md-1',
mimeType: 'text/markdown',
title: 'Readme',
content: '# Title\n\nParagraph.',
};
render(
);
expect(screen.getByText(/# Title/)).toBeInTheDocument();
});
});
describe('Word / fallback', () => {
it('shows "Preview not available" for Word docs with Open and Download', () => {
const medium: Medium = {
mediumID: 'doc-1',
mimeType: 'application/msword',
title: 'Report.doc',
url: 'https://example.com/report.doc',
};
render(
);
expect(screen.getByText(/Preview not available for this document/)).toBeInTheDocument();
expect(screen.getByText(/Open in new tab/)).toBeInTheDocument();
expect(screen.getByText(/Download/)).toBeInTheDocument();
});
it('shows generic fallback for unknown file type', () => {
const medium: Medium = {
mediumID: 'unk-1',
mimeType: 'application/octet-stream',
title: 'file.bin',
url: 'https://example.com/file.bin',
};
render(
);
expect(screen.getByText(/Preview not available for this file type/)).toBeInTheDocument();
});
});
describe('document attachment content', () => {
it('renders document attachment text in Snippet', () => {
const medium: Medium = {
mediumID: 'att-1',
mimeType: 'text/plain',
title: 'Extracted',
content: 'Extracted text from PDF',
properties: { isDocumentAttachment: true },
};
render(
);
expect(screen.getByText(/Extracted text from PDF/)).toBeInTheDocument();
});
});
describe('onClose', () => {
it('modal can be closed via ContentPreviewModal', () => {
const medium: Medium = {
mediumID: 'img-1',
mimeType: 'image/jpeg',
title: 'Photo',
url: 'https://example.com/photo.jpg',
};
render(
);
const closeWrapper = document.querySelector('.memori-modal--close');
expect(closeWrapper).toBeInTheDocument();
const closeButton = closeWrapper?.querySelector('button');
if (closeButton) {
fireEvent.click(closeButton);
expect(onClose).toHaveBeenCalled();
}
});
});
});