generated from coulomb/repo-seed
Implements all requirements from ProductRequirementsDocument.md: - PDF detection via Chrome Downloads API - Secure credential storage with AES-GCM encryption - Binect API integration for PDF uploads - Popup UI with Binect branding - Local transfer tracking (500 entry cap) - Help page with tracking view and CSV export - 60-day credential retention with auto-expiry - Accessibility compliance (WCAG 2.1 AA) Technical implementation: - Chrome Extension Manifest V3 - TypeScript with strict mode - Webpack build system - Jest test suite (22/22 passing) - ESLint configured (0 errors) Build output: 13 KB total (production minified) Test coverage: crypto, pdf-detector, tracker, binect-api Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
104 lines
2.5 KiB
TypeScript
104 lines
2.5 KiB
TypeScript
/**
|
|
* Tests for PDF detection
|
|
*/
|
|
|
|
import { getLastPDFDownload, fetchPDFBytes } from '../src/utils/pdf-detector';
|
|
|
|
// Chrome API is mocked in setup.ts
|
|
|
|
describe('PDF Detector', () => {
|
|
beforeEach(() => {
|
|
jest.clearAllMocks();
|
|
});
|
|
|
|
test('should detect PDF by extension', async () => {
|
|
const mockItems = [
|
|
{
|
|
id: 1,
|
|
filename: 'document.pdf',
|
|
url: 'https://example.com/doc.pdf',
|
|
fileSize: 1024,
|
|
state: 'complete',
|
|
mime: 'application/pdf'
|
|
}
|
|
];
|
|
|
|
(chrome.downloads.search as jest.Mock).mockImplementation((query, callback) => {
|
|
callback(mockItems);
|
|
});
|
|
|
|
const pdf = await getLastPDFDownload();
|
|
expect(pdf).toBeDefined();
|
|
expect(pdf?.filename).toBe('document.pdf');
|
|
expect(pdf?.sourceDomain).toBe('example.com');
|
|
});
|
|
|
|
test('should return null when no PDF found', async () => {
|
|
const mockItems = [
|
|
{
|
|
id: 1,
|
|
filename: 'document.txt',
|
|
url: 'https://example.com/doc.txt',
|
|
fileSize: 1024,
|
|
state: 'complete',
|
|
mime: 'text/plain'
|
|
}
|
|
];
|
|
|
|
(chrome.downloads.search as jest.Mock).mockImplementation((query, callback) => {
|
|
callback(mockItems);
|
|
});
|
|
|
|
const pdf = await getLastPDFDownload();
|
|
expect(pdf).toBeNull();
|
|
});
|
|
|
|
test('should detect PDF by MIME type even without .pdf extension', async () => {
|
|
const mockItems = [
|
|
{
|
|
id: 1,
|
|
filename: 'document',
|
|
url: 'https://example.com/doc',
|
|
fileSize: 1024,
|
|
state: 'complete',
|
|
mime: 'application/pdf'
|
|
}
|
|
];
|
|
|
|
(chrome.downloads.search as jest.Mock).mockImplementation((query, callback) => {
|
|
callback(mockItems);
|
|
});
|
|
|
|
const pdf = await getLastPDFDownload();
|
|
expect(pdf).toBeDefined();
|
|
expect(pdf?.filename).toBe('document');
|
|
});
|
|
});
|
|
|
|
describe('fetchPDFBytes', () => {
|
|
test('should throw error on non-200 response', async () => {
|
|
(fetch as jest.Mock).mockResolvedValue({
|
|
ok: false,
|
|
status: 404,
|
|
statusText: 'Not Found'
|
|
});
|
|
|
|
await expect(fetchPDFBytes('https://example.com/doc.pdf')).rejects.toThrow(
|
|
'Failed to fetch PDF: 404 Not Found'
|
|
);
|
|
});
|
|
|
|
test('should throw error on non-PDF content type', async () => {
|
|
(fetch as jest.Mock).mockResolvedValue({
|
|
ok: true,
|
|
headers: {
|
|
get: (name: string) => (name === 'Content-Type' ? 'text/html' : null)
|
|
}
|
|
});
|
|
|
|
await expect(fetchPDFBytes('https://example.com/doc.pdf')).rejects.toThrow(
|
|
'URL did not return a PDF'
|
|
);
|
|
});
|
|
});
|