Files
bookmarksite/tests/test_metadata_functionality.html
Rainer Koschnick 0abee5b794 Add comprehensive database setup and user management system
- 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
2025-07-19 23:21:50 +02:00

417 lines
17 KiB
HTML

<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Bookmark Metadata Functionality Test</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
background-color: #f5f5f5;
}
.test-container {
background: white;
padding: 20px;
border-radius: 8px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
margin-bottom: 20px;
}
.test-result {
padding: 10px;
margin: 5px 0;
border-radius: 4px;
font-weight: bold;
}
.success {
background-color: #d4edda;
color: #155724;
border: 1px solid #c3e6cb;
}
.error {
background-color: #f8d7da;
color: #721c24;
border: 1px solid #f5c6cb;
}
.info {
background-color: #d1ecf1;
color: #0c5460;
border: 1px solid #bee5eb;
}
.test-section {
margin-bottom: 30px;
padding: 15px;
border: 1px solid #ddd;
border-radius: 5px;
}
.test-section h3 {
margin-top: 0;
color: #2c3e50;
}
.bookmark-preview {
background: #f8f9fa;
padding: 15px;
border-radius: 5px;
margin: 10px 0;
border-left: 4px solid #3498db;
}
.metadata-display {
margin-top: 10px;
font-size: 14px;
}
.tag {
background: #e3f2fd;
color: #1976d2;
padding: 2px 6px;
border-radius: 12px;
font-size: 11px;
margin-right: 4px;
}
.rating {
color: #ffc107;
font-size: 16px;
}
.favorite {
color: #e74c3c;
font-size: 16px;
}
</style>
</head>
<body>
<div class="test-container">
<h1>Bookmark Metadata Functionality Test</h1>
<p>This page tests the new bookmark metadata and tagging system functionality.</p>
<div id="testResults"></div>
<div class="test-section">
<h3>Test Bookmark with Metadata</h3>
<div id="bookmarkPreview" class="bookmark-preview">
<!-- Test bookmark will be displayed here -->
</div>
</div>
</div>
<script>
// Create a test instance of BookmarkManager
class TestBookmarkManager {
constructor() {
this.bookmarks = [];
this.currentEditId = null;
this.currentFilter = 'all';
this.searchTimeout = null;
this.virtualScrollThreshold = 100;
this.itemsPerPage = 50;
this.currentPage = 1;
this.isLoading = false;
// Initialize with test data
this.initializeTestData();
}
initializeTestData() {
// Create test bookmarks with metadata
this.bookmarks = [
{
id: 1,
title: "JavaScript Tutorial",
url: "https://developer.mozilla.org/en-US/docs/Web/JavaScript",
folder: "Development",
tags: ["javascript", "tutorial", "web-development", "programming"],
notes: "Comprehensive JavaScript guide from MDN. Great for beginners and advanced developers.",
rating: 5,
favorite: true,
addDate: Date.now() - 86400000, // 1 day ago
lastModified: Date.now() - 3600000, // 1 hour ago
lastVisited: Date.now() - 1800000, // 30 minutes ago
icon: '',
status: 'valid'
},
{
id: 2,
title: "React Documentation",
url: "https://reactjs.org/docs/getting-started.html",
folder: "Development",
tags: ["react", "frontend", "library"],
notes: "Official React documentation with examples and best practices.",
rating: 4,
favorite: false,
addDate: Date.now() - 172800000, // 2 days ago
lastModified: Date.now() - 86400000, // 1 day ago
lastVisited: null,
icon: '',
status: 'valid'
},
{
id: 3,
title: "Favorite Recipe Blog",
url: "https://example-recipes.com",
folder: "Personal",
tags: ["cooking", "recipes", "food"],
notes: "Amazing collection of healthy recipes. Check the dessert section!",
rating: 3,
favorite: true,
addDate: Date.now() - 259200000, // 3 days ago
lastModified: Date.now() - 172800000, // 2 days ago
lastVisited: Date.now() - 7200000, // 2 hours ago
icon: '',
status: 'unknown'
}
];
}
// Helper function to format relative time
formatRelativeTime(timestamp) {
const now = Date.now();
const diff = now - timestamp;
const minutes = Math.floor(diff / (1000 * 60));
const hours = Math.floor(diff / (1000 * 60 * 60));
const days = Math.floor(diff / (1000 * 60 * 60 * 24));
if (minutes < 60) {
return `${minutes} minute${minutes !== 1 ? 's' : ''} ago`;
} else if (hours < 24) {
return `${hours} hour${hours !== 1 ? 's' : ''} ago`;
} else {
return `${days} day${days !== 1 ? 's' : ''} ago`;
}
}
// Test search functionality with metadata
searchBookmarks(query) {
const lowerQuery = query.toLowerCase();
return this.bookmarks.filter(bookmark => {
if (bookmark.title.toLowerCase().includes(lowerQuery)) return true;
if (bookmark.url.toLowerCase().includes(lowerQuery)) return true;
if (bookmark.folder && bookmark.folder.toLowerCase().includes(lowerQuery)) return true;
if (bookmark.tags && bookmark.tags.some(tag => tag.toLowerCase().includes(lowerQuery))) return true;
if (bookmark.notes && bookmark.notes.toLowerCase().includes(lowerQuery)) return true;
return false;
});
}
// Test filtering by favorites
getFavoriteBookmarks() {
return this.bookmarks.filter(b => b.favorite);
}
// Test filtering by rating
getBookmarksByRating(minRating) {
return this.bookmarks.filter(b => b.rating >= minRating);
}
// Test export functionality with metadata
generateJSONExport(bookmarksToExport) {
const exportData = {
exportDate: new Date().toISOString(),
version: '1.1',
totalBookmarks: bookmarksToExport.length,
bookmarks: bookmarksToExport.map(bookmark => ({
id: bookmark.id,
title: bookmark.title,
url: bookmark.url,
folder: bookmark.folder || '',
tags: bookmark.tags || [],
notes: bookmark.notes || '',
rating: bookmark.rating || 0,
favorite: bookmark.favorite || false,
addDate: bookmark.addDate,
lastModified: bookmark.lastModified,
lastVisited: bookmark.lastVisited,
icon: bookmark.icon || '',
status: bookmark.status
}))
};
return JSON.stringify(exportData, null, 2);
}
}
// Run tests
function runTests() {
const testManager = new TestBookmarkManager();
let results = '<h2>Test Results</h2>';
// Test 1: Basic metadata storage
try {
const bookmark = testManager.bookmarks[0];
const hasAllMetadata = bookmark.tags && bookmark.notes &&
typeof bookmark.rating === 'number' &&
typeof bookmark.favorite === 'boolean';
results += `
<div class="test-result ${hasAllMetadata ? 'success' : 'error'}">
Metadata Storage Test: ${hasAllMetadata ? 'PASSED' : 'FAILED'}
</div>
`;
} catch (error) {
results += `
<div class="test-result error">
Metadata Storage Test: FAILED - ${error.message}
</div>
`;
}
// Test 2: Search functionality with tags
try {
const searchResults = testManager.searchBookmarks('javascript');
const foundByTag = searchResults.length > 0 &&
searchResults.some(b => b.tags.includes('javascript'));
results += `
<div class="test-result ${foundByTag ? 'success' : 'error'}">
Tag Search Test: ${foundByTag ? 'PASSED' : 'FAILED'} (Found ${searchResults.length} results)
</div>
`;
} catch (error) {
results += `
<div class="test-result error">
Tag Search Test: FAILED - ${error.message}
</div>
`;
}
// Test 3: Search functionality with notes
try {
const searchResults = testManager.searchBookmarks('healthy recipes');
const foundByNotes = searchResults.length > 0;
results += `
<div class="test-result ${foundByNotes ? 'success' : 'error'}">
Notes Search Test: ${foundByNotes ? 'PASSED' : 'FAILED'} (Found ${searchResults.length} results)
</div>
`;
} catch (error) {
results += `
<div class="test-result error">
Notes Search Test: FAILED - ${error.message}
</div>
`;
}
// Test 4: Favorite filtering
try {
const favorites = testManager.getFavoriteBookmarks();
const correctFavorites = favorites.length === 2; // Should find 2 favorites
results += `
<div class="test-result ${correctFavorites ? 'success' : 'error'}">
Favorite Filter Test: ${correctFavorites ? 'PASSED' : 'FAILED'} (Found ${favorites.length} favorites)
</div>
`;
} catch (error) {
results += `
<div class="test-result error">
Favorite Filter Test: FAILED - ${error.message}
</div>
`;
}
// Test 5: Rating filtering
try {
const highRated = testManager.getBookmarksByRating(4);
const correctRating = highRated.length === 2; // Should find 2 bookmarks with rating >= 4
results += `
<div class="test-result ${correctRating ? 'success' : 'error'}">
Rating Filter Test: ${correctRating ? 'PASSED' : 'FAILED'} (Found ${highRated.length} high-rated bookmarks)
</div>
`;
} catch (error) {
results += `
<div class="test-result error">
Rating Filter Test: FAILED - ${error.message}
</div>
`;
}
// Test 6: JSON export with metadata
try {
const jsonExport = testManager.generateJSONExport(testManager.bookmarks);
const parsed = JSON.parse(jsonExport);
const hasMetadataInExport = parsed.bookmarks[0].tags &&
parsed.bookmarks[0].notes &&
typeof parsed.bookmarks[0].rating === 'number' &&
typeof parsed.bookmarks[0].favorite === 'boolean';
results += `
<div class="test-result ${hasMetadataInExport ? 'success' : 'error'}">
JSON Export Test: ${hasMetadataInExport ? 'PASSED' : 'FAILED'}
</div>
`;
} catch (error) {
results += `
<div class="test-result error">
JSON Export Test: FAILED - ${error.message}
</div>
`;
}
// Test 7: Last visited tracking
try {
const bookmark = testManager.bookmarks[0];
const hasLastVisited = bookmark.lastVisited && typeof bookmark.lastVisited === 'number';
results += `
<div class="test-result ${hasLastVisited ? 'success' : 'error'}">
Last Visited Tracking Test: ${hasLastVisited ? 'PASSED' : 'FAILED'}
</div>
`;
} catch (error) {
results += `
<div class="test-result error">
Last Visited Tracking Test: FAILED - ${error.message}
</div>
`;
}
document.getElementById('testResults').innerHTML = results;
// Display test bookmark with metadata
displayTestBookmark(testManager.bookmarks[0], testManager);
}
function displayTestBookmark(bookmark, manager) {
const preview = document.getElementById('bookmarkPreview');
let html = `
<h4>${bookmark.title}</h4>
<div style="color: #666; margin-bottom: 10px;">${bookmark.url}</div>
<div style="color: #3498db; font-size: 12px; margin-bottom: 10px;">Folder: ${bookmark.folder}</div>
<div class="metadata-display">
<div style="margin-bottom: 8px;">
<strong>Tags:</strong>
${bookmark.tags.map(tag => `<span class="tag">${tag}</span>`).join('')}
</div>
<div style="margin-bottom: 8px;">
<strong>Rating:</strong>
<span class="rating">${'★'.repeat(bookmark.rating)}${'☆'.repeat(5 - bookmark.rating)}</span>
${bookmark.favorite ? '<span class="favorite">❤️ Favorite</span>' : ''}
</div>
<div style="margin-bottom: 8px;">
<strong>Notes:</strong>
<div style="font-style: italic; color: #666; margin-top: 4px;">${bookmark.notes}</div>
</div>
<div style="font-size: 12px; color: #999;">
<div>Added: ${manager.formatRelativeTime(bookmark.addDate)}</div>
<div>Last modified: ${manager.formatRelativeTime(bookmark.lastModified)}</div>
<div>Last visited: ${manager.formatRelativeTime(bookmark.lastVisited)}</div>
</div>
</div>
`;
preview.innerHTML = html;
}
// Run tests when page loads
document.addEventListener('DOMContentLoaded', runTests);
</script>
</body>
</html>