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
This commit is contained in:
339
tests/test_organization_features.html
Normal file
339
tests/test_organization_features.html
Normal file
@ -0,0 +1,339 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Test Organization Features - Bookmark Manager</title>
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<style>
|
||||
.test-section {
|
||||
background: white;
|
||||
padding: 20px;
|
||||
margin: 20px 0;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.test-section h3 {
|
||||
color: #2c3e50;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
.test-button {
|
||||
margin: 5px;
|
||||
padding: 10px 15px;
|
||||
background: #3498db;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
.test-button:hover {
|
||||
background: #2980b9;
|
||||
}
|
||||
.test-results {
|
||||
margin-top: 15px;
|
||||
padding: 10px;
|
||||
background: #f8f9fa;
|
||||
border-radius: 4px;
|
||||
font-family: monospace;
|
||||
font-size: 14px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>Organization Features Test</h1>
|
||||
</header>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>Test Data Setup</h3>
|
||||
<button class="test-button" onclick="createTestBookmarks()">Create Test Bookmarks</button>
|
||||
<button class="test-button" onclick="clearTestData()">Clear Test Data</button>
|
||||
<div id="setupResults" class="test-results"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>Drag and Drop Test</h3>
|
||||
<p>After creating test bookmarks, try dragging bookmarks between folders to test the drag-and-drop functionality.</p>
|
||||
<button class="test-button" onclick="testDragAndDrop()">Test Programmatic Move</button>
|
||||
<div id="dragDropResults" class="test-results"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>Bulk Operations Test</h3>
|
||||
<button class="test-button" onclick="testBulkMode()">Toggle Bulk Mode</button>
|
||||
<button class="test-button" onclick="testBulkSelection()">Test Bulk Selection</button>
|
||||
<button class="test-button" onclick="testBulkMove()">Test Bulk Move</button>
|
||||
<div id="bulkResults" class="test-results"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>Sorting Test</h3>
|
||||
<button class="test-button" onclick="testSortByTitle()">Sort by Title</button>
|
||||
<button class="test-button" onclick="testSortByDate()">Sort by Date</button>
|
||||
<button class="test-button" onclick="testSortByFolder()">Sort by Folder</button>
|
||||
<div id="sortResults" class="test-results"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h3>Folder Management Test</h3>
|
||||
<button class="test-button" onclick="testCreateFolder()">Create Test Folder</button>
|
||||
<button class="test-button" onclick="testRenameFolder()">Rename Folder</button>
|
||||
<button class="test-button" onclick="testMergeFolder()">Merge Folders</button>
|
||||
<button class="test-button" onclick="testDeleteFolder()">Delete Folder</button>
|
||||
<div id="folderResults" class="test-results"></div>
|
||||
</div>
|
||||
|
||||
<!-- Include the main bookmark manager interface -->
|
||||
<div id="bookmarksList" class="bookmarks-list"></div>
|
||||
|
||||
<!-- Bulk Actions Bar -->
|
||||
<div id="bulkActions" class="bulk-actions" style="display: none;">
|
||||
<div class="bulk-selection-info">
|
||||
<span class="selection-count">0 selected</span>
|
||||
</div>
|
||||
<div class="bulk-action-buttons">
|
||||
<select id="bulkMoveFolder" class="bulk-folder-select">
|
||||
<option value="">Move to folder...</option>
|
||||
</select>
|
||||
<button id="bulkMoveBtn" class="btn btn-secondary" disabled>Move</button>
|
||||
<button id="bulkDeleteBtn" class="btn btn-danger" disabled>Delete Selected</button>
|
||||
<button id="bulkSelectAllBtn" class="btn btn-secondary">Select All</button>
|
||||
<button id="bulkClearSelectionBtn" class="btn btn-secondary">Clear Selection</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script src="script.js"></script>
|
||||
<script>
|
||||
let testManager;
|
||||
|
||||
// Initialize test manager
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
testManager = new BookmarkManager();
|
||||
});
|
||||
|
||||
function createTestBookmarks() {
|
||||
const testBookmarks = [
|
||||
{
|
||||
id: Date.now() + 1,
|
||||
title: 'Google',
|
||||
url: 'https://www.google.com',
|
||||
folder: 'Search Engines',
|
||||
addDate: Date.now() - 86400000, // 1 day ago
|
||||
icon: '',
|
||||
status: 'unknown'
|
||||
},
|
||||
{
|
||||
id: Date.now() + 2,
|
||||
title: 'GitHub',
|
||||
url: 'https://github.com',
|
||||
folder: 'Development',
|
||||
addDate: Date.now() - 172800000, // 2 days ago
|
||||
icon: '',
|
||||
status: 'unknown'
|
||||
},
|
||||
{
|
||||
id: Date.now() + 3,
|
||||
title: 'Stack Overflow',
|
||||
url: 'https://stackoverflow.com',
|
||||
folder: 'Development',
|
||||
addDate: Date.now() - 259200000, // 3 days ago
|
||||
icon: '',
|
||||
status: 'unknown'
|
||||
},
|
||||
{
|
||||
id: Date.now() + 4,
|
||||
title: 'YouTube',
|
||||
url: 'https://www.youtube.com',
|
||||
folder: 'Entertainment',
|
||||
addDate: Date.now(),
|
||||
icon: '',
|
||||
status: 'unknown'
|
||||
},
|
||||
{
|
||||
id: Date.now() + 5,
|
||||
title: 'Wikipedia',
|
||||
url: 'https://www.wikipedia.org',
|
||||
folder: '', // No folder
|
||||
addDate: Date.now() - 345600000, // 4 days ago
|
||||
icon: '',
|
||||
status: 'unknown'
|
||||
}
|
||||
];
|
||||
|
||||
testManager.bookmarks = testBookmarks;
|
||||
testManager.saveBookmarksToStorage();
|
||||
testManager.renderBookmarks();
|
||||
testManager.updateStats();
|
||||
|
||||
document.getElementById('setupResults').textContent =
|
||||
`Created ${testBookmarks.length} test bookmarks across multiple folders.`;
|
||||
}
|
||||
|
||||
function clearTestData() {
|
||||
testManager.bookmarks = [];
|
||||
testManager.saveBookmarksToStorage();
|
||||
testManager.renderBookmarks();
|
||||
testManager.updateStats();
|
||||
|
||||
document.getElementById('setupResults').textContent = 'Test data cleared.';
|
||||
}
|
||||
|
||||
function testDragAndDrop() {
|
||||
if (testManager.bookmarks.length === 0) {
|
||||
document.getElementById('dragDropResults').textContent = 'No bookmarks to test. Create test bookmarks first.';
|
||||
return;
|
||||
}
|
||||
|
||||
// Test programmatic move
|
||||
const bookmark = testManager.bookmarks[0];
|
||||
const oldFolder = bookmark.folder;
|
||||
const newFolder = 'Test Folder';
|
||||
|
||||
testManager.moveBookmarkToFolder(bookmark.id, newFolder);
|
||||
|
||||
document.getElementById('dragDropResults').textContent =
|
||||
`Moved "${bookmark.title}" from "${oldFolder || 'Uncategorized'}" to "${newFolder}". Check the UI to verify the change.`;
|
||||
}
|
||||
|
||||
function testBulkMode() {
|
||||
testManager.toggleBulkMode();
|
||||
const isActive = testManager.bulkMode;
|
||||
|
||||
document.getElementById('bulkResults').textContent =
|
||||
`Bulk mode ${isActive ? 'activated' : 'deactivated'}. ${isActive ? 'Checkboxes should now be visible on bookmark items.' : 'Checkboxes should be hidden.'}`;
|
||||
}
|
||||
|
||||
function testBulkSelection() {
|
||||
if (!testManager.bulkMode) {
|
||||
document.getElementById('bulkResults').textContent = 'Bulk mode must be active first.';
|
||||
return;
|
||||
}
|
||||
|
||||
if (testManager.bookmarks.length === 0) {
|
||||
document.getElementById('bulkResults').textContent = 'No bookmarks to select. Create test bookmarks first.';
|
||||
return;
|
||||
}
|
||||
|
||||
// Select first two bookmarks
|
||||
const firstTwo = testManager.bookmarks.slice(0, 2);
|
||||
firstTwo.forEach(bookmark => {
|
||||
testManager.toggleBookmarkSelection(bookmark.id);
|
||||
});
|
||||
|
||||
document.getElementById('bulkResults').textContent =
|
||||
`Selected ${firstTwo.length} bookmarks: ${firstTwo.map(b => b.title).join(', ')}`;
|
||||
}
|
||||
|
||||
function testBulkMove() {
|
||||
if (testManager.bulkSelection.size === 0) {
|
||||
document.getElementById('bulkResults').textContent = 'No bookmarks selected. Use bulk selection test first.';
|
||||
return;
|
||||
}
|
||||
|
||||
const targetFolder = 'Bulk Moved';
|
||||
const selectedCount = testManager.bulkSelection.size;
|
||||
|
||||
testManager.bulkMoveToFolder(targetFolder);
|
||||
|
||||
document.getElementById('bulkResults').textContent =
|
||||
`Moved ${selectedCount} bookmarks to "${targetFolder}" folder.`;
|
||||
}
|
||||
|
||||
function testSortByTitle() {
|
||||
if (testManager.bookmarks.length === 0) {
|
||||
document.getElementById('sortResults').textContent = 'No bookmarks to sort. Create test bookmarks first.';
|
||||
return;
|
||||
}
|
||||
|
||||
const beforeTitles = testManager.bookmarks.map(b => b.title);
|
||||
testManager.sortBookmarks('title', 'asc');
|
||||
const afterTitles = testManager.bookmarks.map(b => b.title);
|
||||
|
||||
document.getElementById('sortResults').textContent =
|
||||
`Sorted by title. Before: [${beforeTitles.join(', ')}] After: [${afterTitles.join(', ')}]`;
|
||||
}
|
||||
|
||||
function testSortByDate() {
|
||||
if (testManager.bookmarks.length === 0) {
|
||||
document.getElementById('sortResults').textContent = 'No bookmarks to sort. Create test bookmarks first.';
|
||||
return;
|
||||
}
|
||||
|
||||
testManager.sortBookmarks('date', 'desc');
|
||||
const sortedTitles = testManager.bookmarks.map(b => b.title);
|
||||
|
||||
document.getElementById('sortResults').textContent =
|
||||
`Sorted by date (newest first): [${sortedTitles.join(', ')}]`;
|
||||
}
|
||||
|
||||
function testSortByFolder() {
|
||||
if (testManager.bookmarks.length === 0) {
|
||||
document.getElementById('sortResults').textContent = 'No bookmarks to sort. Create test bookmarks first.';
|
||||
return;
|
||||
}
|
||||
|
||||
testManager.sortBookmarks('folder', 'asc');
|
||||
const sortedByFolder = testManager.bookmarks.map(b => `${b.title} (${b.folder || 'No folder'})`);
|
||||
|
||||
document.getElementById('sortResults').textContent =
|
||||
`Sorted by folder: [${sortedByFolder.join(', ')}]`;
|
||||
}
|
||||
|
||||
function testCreateFolder() {
|
||||
const folderName = 'Test Created Folder';
|
||||
testManager.createFolder(folderName);
|
||||
|
||||
document.getElementById('folderResults').textContent =
|
||||
`Created folder "${folderName}". Check the bookmark list for the new folder.`;
|
||||
}
|
||||
|
||||
function testRenameFolder() {
|
||||
const folders = Object.keys(testManager.getFolderStats());
|
||||
if (folders.length === 0) {
|
||||
document.getElementById('folderResults').textContent = 'No folders to rename. Create test bookmarks first.';
|
||||
return;
|
||||
}
|
||||
|
||||
const oldName = folders[0];
|
||||
const newName = oldName + ' (Renamed)';
|
||||
|
||||
testManager.renameFolder(oldName, newName);
|
||||
|
||||
document.getElementById('folderResults').textContent =
|
||||
`Renamed folder "${oldName}" to "${newName}".`;
|
||||
}
|
||||
|
||||
function testMergeFolder() {
|
||||
const folders = Object.keys(testManager.getFolderStats());
|
||||
if (folders.length < 2) {
|
||||
document.getElementById('folderResults').textContent = 'Need at least 2 folders to test merge. Create more test bookmarks.';
|
||||
return;
|
||||
}
|
||||
|
||||
const sourceFolder = folders[0];
|
||||
const targetFolder = folders[1];
|
||||
|
||||
testManager.mergeFolder(sourceFolder, targetFolder);
|
||||
|
||||
document.getElementById('folderResults').textContent =
|
||||
`Merged folder "${sourceFolder}" into "${targetFolder}".`;
|
||||
}
|
||||
|
||||
function testDeleteFolder() {
|
||||
const folders = Object.keys(testManager.getFolderStats());
|
||||
if (folders.length === 0) {
|
||||
document.getElementById('folderResults').textContent = 'No folders to delete. Create test bookmarks first.';
|
||||
return;
|
||||
}
|
||||
|
||||
const folderToDelete = folders[0];
|
||||
testManager.deleteFolder(folderToDelete);
|
||||
|
||||
document.getElementById('folderResults').textContent =
|
||||
`Deleted folder "${folderToDelete}". Bookmarks moved to Uncategorized.`;
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user