- Implement PostgreSQL database schema with users and bookmarks tables - Add database connection pooling with retry logic and error handling - Create migration system with automatic schema initialization - Add database CLI tools for management (init, status, validate, etc.) - Include comprehensive error handling and diagnostics - Add development seed data and testing utilities - Implement health monitoring and connection pool statistics - Create detailed documentation and troubleshooting guide Database features: - Users table with authentication fields and email verification - Bookmarks table with user association and metadata - Proper indexes for performance optimization - Automatic timestamp triggers - Transaction support with rollback handling - Connection pooling (20 max connections, 30s idle timeout) - Graceful shutdown handling CLI commands available: - npm run db:init - Initialize database - npm run db:status - Check database status - npm run db:validate - Validate schema - npm run db:test - Run database tests - npm run db:diagnostics - Full diagnostics
299 lines
12 KiB
HTML
299 lines
12 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="en">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Advanced Import Test - Bookmark Manager</title>
|
|
<link rel="stylesheet" href="styles.css">
|
|
</head>
|
|
<body>
|
|
<div class="container">
|
|
<h1>Advanced Import/Export Features Test</h1>
|
|
|
|
<div class="test-section">
|
|
<h2>Test Chrome Bookmarks JSON</h2>
|
|
<p>Test the Chrome bookmarks JSON parser with sample data:</p>
|
|
<button id="testChromeParser" class="btn btn-primary">Test Chrome Parser</button>
|
|
<div id="chromeTestResult" class="test-result"></div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h2>Test Firefox Bookmarks JSON</h2>
|
|
<p>Test the Firefox bookmarks JSON parser with sample data:</p>
|
|
<button id="testFirefoxParser" class="btn btn-primary">Test Firefox Parser</button>
|
|
<div id="firefoxTestResult" class="test-result"></div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h2>Test Import Preview</h2>
|
|
<p>Test the import preview functionality:</p>
|
|
<button id="testImportPreview" class="btn btn-primary">Test Import Preview</button>
|
|
<div id="previewTestResult" class="test-result"></div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h2>Test Duplicate Detection</h2>
|
|
<p>Test the enhanced duplicate detection algorithms:</p>
|
|
<button id="testDuplicateDetection" class="btn btn-primary">Test Duplicate Detection</button>
|
|
<div id="duplicateTestResult" class="test-result"></div>
|
|
</div>
|
|
|
|
<div class="test-section">
|
|
<h2>Test Sync Functionality</h2>
|
|
<p>Test the device synchronization features:</p>
|
|
<button id="testSyncFeatures" class="btn btn-primary">Test Sync Features</button>
|
|
<div id="syncTestResult" class="test-result"></div>
|
|
</div>
|
|
</div>
|
|
|
|
<script src="script.js"></script>
|
|
<script>
|
|
// Initialize bookmark manager
|
|
const bookmarkManager = new BookmarkManager();
|
|
|
|
// Test Chrome parser
|
|
document.getElementById('testChromeParser').addEventListener('click', () => {
|
|
const sampleChromeData = {
|
|
"roots": {
|
|
"bookmark_bar": {
|
|
"children": [
|
|
{
|
|
"type": "url",
|
|
"name": "Google",
|
|
"url": "https://www.google.com",
|
|
"date_added": "13285166270000000"
|
|
},
|
|
{
|
|
"type": "folder",
|
|
"name": "Development",
|
|
"children": [
|
|
{
|
|
"type": "url",
|
|
"name": "GitHub",
|
|
"url": "https://github.com",
|
|
"date_added": "13285166280000000"
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
}
|
|
};
|
|
|
|
try {
|
|
const bookmarks = bookmarkManager.parseChromeBookmarks(JSON.stringify(sampleChromeData));
|
|
document.getElementById('chromeTestResult').innerHTML = `
|
|
<div class="test-success">✅ Chrome parser test passed!</div>
|
|
<div>Parsed ${bookmarks.length} bookmarks:</div>
|
|
<ul>
|
|
${bookmarks.map(b => `<li>${b.title} - ${b.url} (${b.folder || 'Root'})</li>`).join('')}
|
|
</ul>
|
|
`;
|
|
} catch (error) {
|
|
document.getElementById('chromeTestResult').innerHTML = `
|
|
<div class="test-error">❌ Chrome parser test failed: ${error.message}</div>
|
|
`;
|
|
}
|
|
});
|
|
|
|
// Test Firefox parser
|
|
document.getElementById('testFirefoxParser').addEventListener('click', () => {
|
|
const sampleFirefoxData = [
|
|
{
|
|
"type": "text/x-moz-place-container",
|
|
"title": "Bookmarks Menu",
|
|
"children": [
|
|
{
|
|
"type": "text/x-moz-place",
|
|
"title": "Mozilla Firefox",
|
|
"uri": "https://www.mozilla.org/firefox/",
|
|
"dateAdded": 1642534567890000
|
|
},
|
|
{
|
|
"type": "text/x-moz-place-container",
|
|
"title": "Development",
|
|
"children": [
|
|
{
|
|
"type": "text/x-moz-place",
|
|
"title": "MDN Web Docs",
|
|
"uri": "https://developer.mozilla.org/",
|
|
"dateAdded": 1642534577890000
|
|
}
|
|
]
|
|
}
|
|
]
|
|
}
|
|
];
|
|
|
|
try {
|
|
const bookmarks = bookmarkManager.parseFirefoxBookmarks(JSON.stringify(sampleFirefoxData));
|
|
document.getElementById('firefoxTestResult').innerHTML = `
|
|
<div class="test-success">✅ Firefox parser test passed!</div>
|
|
<div>Parsed ${bookmarks.length} bookmarks:</div>
|
|
<ul>
|
|
${bookmarks.map(b => `<li>${b.title} - ${b.url} (${b.folder || 'Root'})</li>`).join('')}
|
|
</ul>
|
|
`;
|
|
} catch (error) {
|
|
document.getElementById('firefoxTestResult').innerHTML = `
|
|
<div class="test-error">❌ Firefox parser test failed: ${error.message}</div>
|
|
`;
|
|
}
|
|
});
|
|
|
|
// Test import preview
|
|
document.getElementById('testImportPreview').addEventListener('click', () => {
|
|
const testBookmarks = [
|
|
{
|
|
id: 'test1',
|
|
title: 'Test Bookmark 1',
|
|
url: 'https://example.com',
|
|
folder: 'Test Folder',
|
|
addDate: Date.now(),
|
|
status: 'unknown'
|
|
},
|
|
{
|
|
id: 'test2',
|
|
title: 'Test Bookmark 2',
|
|
url: 'https://test.com',
|
|
folder: 'Another Folder',
|
|
addDate: Date.now(),
|
|
status: 'unknown'
|
|
}
|
|
];
|
|
|
|
const importData = {
|
|
bookmarks: testBookmarks,
|
|
format: 'test',
|
|
originalCount: testBookmarks.length
|
|
};
|
|
|
|
try {
|
|
const analysis = bookmarkManager.analyzeImportData(importData, 'merge');
|
|
document.getElementById('previewTestResult').innerHTML = `
|
|
<div class="test-success">✅ Import preview test passed!</div>
|
|
<div>Analysis results:</div>
|
|
<ul>
|
|
<li>New bookmarks: ${analysis.newBookmarks.length}</li>
|
|
<li>Duplicates: ${analysis.duplicates.length}</li>
|
|
<li>Folders: ${analysis.folders.length}</li>
|
|
</ul>
|
|
`;
|
|
} catch (error) {
|
|
document.getElementById('previewTestResult').innerHTML = `
|
|
<div class="test-error">❌ Import preview test failed: ${error.message}</div>
|
|
`;
|
|
}
|
|
});
|
|
|
|
// Test duplicate detection
|
|
document.getElementById('testDuplicateDetection').addEventListener('click', () => {
|
|
// Add some test bookmarks first
|
|
bookmarkManager.bookmarks = [
|
|
{
|
|
id: 'existing1',
|
|
title: 'Google Search',
|
|
url: 'https://www.google.com/',
|
|
folder: 'Search',
|
|
addDate: Date.now() - 1000000,
|
|
status: 'valid'
|
|
}
|
|
];
|
|
|
|
const testBookmark = {
|
|
title: 'Google',
|
|
url: 'https://google.com',
|
|
folder: 'Web',
|
|
addDate: Date.now(),
|
|
status: 'unknown'
|
|
};
|
|
|
|
try {
|
|
const duplicate = bookmarkManager.findDuplicateBookmark(testBookmark, true, false);
|
|
const similarity = bookmarkManager.calculateStringSimilarity('Google Search', 'Google');
|
|
|
|
document.getElementById('duplicateTestResult').innerHTML = `
|
|
<div class="test-success">✅ Duplicate detection test passed!</div>
|
|
<div>Test results:</div>
|
|
<ul>
|
|
<li>Duplicate found: ${duplicate ? 'Yes' : 'No'}</li>
|
|
<li>Title similarity: ${Math.round(similarity * 100)}%</li>
|
|
<li>URL normalization working: ${bookmarkManager.normalizeUrl('https://www.google.com/') === bookmarkManager.normalizeUrl('https://google.com')}</li>
|
|
</ul>
|
|
`;
|
|
} catch (error) {
|
|
document.getElementById('duplicateTestResult').innerHTML = `
|
|
<div class="test-error">❌ Duplicate detection test failed: ${error.message}</div>
|
|
`;
|
|
}
|
|
});
|
|
|
|
// Test sync functionality
|
|
document.getElementById('testSyncFeatures').addEventListener('click', () => {
|
|
try {
|
|
const deviceId = bookmarkManager.getDeviceId();
|
|
const syncData = bookmarkManager.exportForSync();
|
|
const dataHash = bookmarkManager.calculateDataHash();
|
|
|
|
document.getElementById('syncTestResult').innerHTML = `
|
|
<div class="test-success">✅ Sync features test passed!</div>
|
|
<div>Test results:</div>
|
|
<ul>
|
|
<li>Device ID: ${deviceId}</li>
|
|
<li>Sync data generated: ${syncData.bookmarks.length} bookmarks</li>
|
|
<li>Data hash: ${dataHash}</li>
|
|
<li>Compression working: ${bookmarkManager.compressAndEncodeData({test: 'data'}).length > 0}</li>
|
|
</ul>
|
|
`;
|
|
} catch (error) {
|
|
document.getElementById('syncTestResult').innerHTML = `
|
|
<div class="test-error">❌ Sync features test failed: ${error.message}</div>
|
|
`;
|
|
}
|
|
});
|
|
</script>
|
|
|
|
<style>
|
|
.test-section {
|
|
margin: 20px 0;
|
|
padding: 20px;
|
|
border: 1px solid #ddd;
|
|
border-radius: 8px;
|
|
background-color: #f9f9f9;
|
|
}
|
|
|
|
.test-result {
|
|
margin-top: 15px;
|
|
padding: 10px;
|
|
border-radius: 4px;
|
|
}
|
|
|
|
.test-success {
|
|
color: #155724;
|
|
background-color: #d4edda;
|
|
border: 1px solid #c3e6cb;
|
|
padding: 8px 12px;
|
|
border-radius: 4px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.test-error {
|
|
color: #721c24;
|
|
background-color: #f8d7da;
|
|
border: 1px solid #f5c6cb;
|
|
padding: 8px 12px;
|
|
border-radius: 4px;
|
|
margin-bottom: 10px;
|
|
}
|
|
|
|
.test-result ul {
|
|
margin: 10px 0;
|
|
padding-left: 20px;
|
|
}
|
|
|
|
.test-result li {
|
|
margin: 5px 0;
|
|
}
|
|
</style>
|
|
</body>
|
|
</html> |