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:
786
tests/test_analytics_functionality.html
Normal file
786
tests/test_analytics_functionality.html
Normal file
@ -0,0 +1,786 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>Analytics Functionality Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
background-color: #f5f5f5;
|
||||
}
|
||||
.test-section {
|
||||
background: white;
|
||||
margin: 20px 0;
|
||||
padding: 20px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
|
||||
}
|
||||
.test-result {
|
||||
padding: 10px;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
}
|
||||
.test-pass {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
.test-fail {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
.test-info {
|
||||
background-color: #d1ecf1;
|
||||
color: #0c5460;
|
||||
border: 1px solid #bee5eb;
|
||||
}
|
||||
button {
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 20px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin: 5px;
|
||||
}
|
||||
button:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
.analytics-preview {
|
||||
border: 1px solid #ddd;
|
||||
padding: 15px;
|
||||
margin: 10px 0;
|
||||
border-radius: 4px;
|
||||
background: #f8f9fa;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>Analytics Functionality Test</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Test Setup</h2>
|
||||
<p>This test verifies the enhanced statistics and reporting features implementation.</p>
|
||||
<button onclick="setupTestData()">Setup Test Data</button>
|
||||
<button onclick="clearTestData()">Clear Test Data</button>
|
||||
<div id="setupResults"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Analytics Dashboard Tests</h2>
|
||||
<button onclick="testAnalyticsButton()">Test Analytics Button</button>
|
||||
<button onclick="testAnalyticsModal()">Test Analytics Modal</button>
|
||||
<button onclick="testAnalyticsTabs()">Test Analytics Tabs</button>
|
||||
<div id="analyticsResults"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Overview Tab Tests</h2>
|
||||
<button onclick="testOverviewTab()">Test Overview Analytics</button>
|
||||
<button onclick="testStatusChart()">Test Status Chart</button>
|
||||
<button onclick="testFoldersChart()">Test Folders Chart</button>
|
||||
<div id="overviewResults"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Trends Tab Tests</h2>
|
||||
<button onclick="testTrendsTab()">Test Trends Analytics</button>
|
||||
<button onclick="testTrendsChart()">Test Trends Chart</button>
|
||||
<button onclick="testTestingTrendsChart()">Test Testing Trends Chart</button>
|
||||
<div id="trendsResults"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Health Report Tests</h2>
|
||||
<button onclick="testHealthTab()">Test Health Analytics</button>
|
||||
<button onclick="testHealthMetrics()">Test Health Metrics</button>
|
||||
<button onclick="testHealthRecommendations()">Test Health Recommendations</button>
|
||||
<div id="healthResults"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Usage Patterns Tests</h2>
|
||||
<button onclick="testUsageTab()">Test Usage Analytics</button>
|
||||
<button onclick="testUsageMetrics()">Test Usage Metrics</button>
|
||||
<button onclick="testUsageCharts()">Test Usage Charts</button>
|
||||
<div id="usageResults"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Export Functionality Tests</h2>
|
||||
<button onclick="testAnalyticsExport()">Test Analytics Data Export</button>
|
||||
<button onclick="testReportGeneration()">Test Report Generation</button>
|
||||
<div id="exportResults"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Chart Drawing Tests</h2>
|
||||
<button onclick="testChartDrawing()">Test Chart Drawing Functions</button>
|
||||
<div id="chartResults"></div>
|
||||
</div>
|
||||
|
||||
<!-- Include the main application files -->
|
||||
<link rel="stylesheet" href="styles.css">
|
||||
<script src="script.js"></script>
|
||||
|
||||
<script>
|
||||
// Initialize bookmark manager for testing
|
||||
let testBookmarkManager;
|
||||
|
||||
function initializeTestManager() {
|
||||
if (!testBookmarkManager) {
|
||||
testBookmarkManager = new BookmarkManager();
|
||||
}
|
||||
return testBookmarkManager;
|
||||
}
|
||||
|
||||
function setupTestData() {
|
||||
const manager = initializeTestManager();
|
||||
const results = document.getElementById('setupResults');
|
||||
|
||||
// Clear existing bookmarks
|
||||
manager.bookmarks = [];
|
||||
|
||||
// Create test bookmarks with various statuses, folders, and metadata
|
||||
const testBookmarks = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Google',
|
||||
url: 'https://google.com',
|
||||
folder: 'Search Engines',
|
||||
status: 'valid',
|
||||
addDate: Date.now() - (7 * 24 * 60 * 60 * 1000), // 7 days ago
|
||||
lastTested: Date.now() - (1 * 24 * 60 * 60 * 1000), // 1 day ago
|
||||
rating: 5,
|
||||
favorite: true,
|
||||
visitCount: 25
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Broken Link',
|
||||
url: 'https://broken-link-example.com',
|
||||
folder: 'Test',
|
||||
status: 'invalid',
|
||||
addDate: Date.now() - (30 * 24 * 60 * 60 * 1000), // 30 days ago
|
||||
lastTested: Date.now() - (2 * 24 * 60 * 60 * 1000), // 2 days ago
|
||||
rating: 2,
|
||||
favorite: false,
|
||||
visitCount: 3
|
||||
},
|
||||
{
|
||||
id: '3',
|
||||
title: 'GitHub',
|
||||
url: 'https://github.com',
|
||||
folder: 'Development',
|
||||
status: 'valid',
|
||||
addDate: Date.now() - (15 * 24 * 60 * 60 * 1000), // 15 days ago
|
||||
lastTested: Date.now() - (3 * 24 * 60 * 60 * 1000), // 3 days ago
|
||||
rating: 4,
|
||||
favorite: true,
|
||||
visitCount: 50
|
||||
},
|
||||
{
|
||||
id: '4',
|
||||
title: 'Duplicate Google',
|
||||
url: 'https://www.google.com/',
|
||||
folder: 'Search Engines',
|
||||
status: 'duplicate',
|
||||
addDate: Date.now() - (5 * 24 * 60 * 60 * 1000), // 5 days ago
|
||||
rating: 3,
|
||||
favorite: false,
|
||||
visitCount: 1
|
||||
},
|
||||
{
|
||||
id: '5',
|
||||
title: 'Untested Link',
|
||||
url: 'https://example.com',
|
||||
folder: 'Uncategorized',
|
||||
status: 'unknown',
|
||||
addDate: Date.now() - (2 * 24 * 60 * 60 * 1000), // 2 days ago
|
||||
rating: 0,
|
||||
favorite: false,
|
||||
visitCount: 0
|
||||
},
|
||||
{
|
||||
id: '6',
|
||||
title: 'Old Bookmark',
|
||||
url: 'https://old-site.com',
|
||||
folder: 'Archive',
|
||||
status: 'valid',
|
||||
addDate: Date.now() - (800 * 24 * 60 * 60 * 1000), // 800 days ago (over 2 years)
|
||||
lastTested: Date.now() - (100 * 24 * 60 * 60 * 1000), // 100 days ago
|
||||
rating: 1,
|
||||
favorite: false,
|
||||
visitCount: 2
|
||||
}
|
||||
];
|
||||
|
||||
manager.bookmarks = testBookmarks;
|
||||
manager.saveBookmarksToStorage();
|
||||
manager.updateStats();
|
||||
|
||||
results.innerHTML = `
|
||||
<div class="test-pass">✓ Test data setup complete</div>
|
||||
<div class="test-info">Created ${testBookmarks.length} test bookmarks with various statuses, folders, and metadata</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function clearTestData() {
|
||||
const manager = initializeTestManager();
|
||||
const results = document.getElementById('setupResults');
|
||||
|
||||
manager.bookmarks = [];
|
||||
manager.saveBookmarksToStorage();
|
||||
manager.updateStats();
|
||||
|
||||
results.innerHTML = `
|
||||
<div class="test-pass">✓ Test data cleared</div>
|
||||
`;
|
||||
}
|
||||
|
||||
function testAnalyticsButton() {
|
||||
const results = document.getElementById('analyticsResults');
|
||||
let testResults = [];
|
||||
|
||||
// Check if analytics button exists
|
||||
const analyticsBtn = document.getElementById('analyticsBtn');
|
||||
if (analyticsBtn) {
|
||||
testResults.push('<div class="test-pass">✓ Analytics button found in UI</div>');
|
||||
|
||||
// Check if button has correct attributes
|
||||
if (analyticsBtn.classList.contains('btn') && analyticsBtn.classList.contains('btn-info')) {
|
||||
testResults.push('<div class="test-pass">✓ Analytics button has correct styling classes</div>');
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ Analytics button missing correct styling classes</div>');
|
||||
}
|
||||
|
||||
// Check aria-label
|
||||
if (analyticsBtn.getAttribute('aria-label')) {
|
||||
testResults.push('<div class="test-pass">✓ Analytics button has accessibility label</div>');
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ Analytics button missing accessibility label</div>');
|
||||
}
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ Analytics button not found in UI</div>');
|
||||
}
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testAnalyticsModal() {
|
||||
const results = document.getElementById('analyticsResults');
|
||||
let testResults = [];
|
||||
|
||||
// Check if analytics modal exists
|
||||
const analyticsModal = document.getElementById('analyticsModal');
|
||||
if (analyticsModal) {
|
||||
testResults.push('<div class="test-pass">✓ Analytics modal found in DOM</div>');
|
||||
|
||||
// Check modal structure
|
||||
const modalContent = analyticsModal.querySelector('.modal-content');
|
||||
if (modalContent && modalContent.classList.contains('analytics-modal-content')) {
|
||||
testResults.push('<div class="test-pass">✓ Analytics modal has correct content structure</div>');
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ Analytics modal missing correct content structure</div>');
|
||||
}
|
||||
|
||||
// Check for tabs
|
||||
const tabs = analyticsModal.querySelectorAll('.analytics-tab');
|
||||
if (tabs.length === 4) {
|
||||
testResults.push('<div class="test-pass">✓ Analytics modal has all 4 tabs (Overview, Trends, Health, Usage)</div>');
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ Analytics modal missing tabs (found ' + tabs.length + ', expected 4)</div>');
|
||||
}
|
||||
|
||||
// Check for tab contents
|
||||
const tabContents = analyticsModal.querySelectorAll('.analytics-tab-content');
|
||||
if (tabContents.length === 4) {
|
||||
testResults.push('<div class="test-pass">✓ Analytics modal has all 4 tab contents</div>');
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ Analytics modal missing tab contents (found ' + tabContents.length + ', expected 4)</div>');
|
||||
}
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ Analytics modal not found in DOM</div>');
|
||||
}
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testAnalyticsTabs() {
|
||||
const results = document.getElementById('analyticsResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test if analytics methods exist
|
||||
const methods = ['showAnalyticsModal', 'initializeAnalytics', 'bindAnalyticsTabEvents', 'loadTabAnalytics'];
|
||||
methods.forEach(method => {
|
||||
if (typeof manager[method] === 'function') {
|
||||
testResults.push(`<div class="test-pass">✓ Method ${method} exists</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Method ${method} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testOverviewTab() {
|
||||
const results = document.getElementById('overviewResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test overview analytics methods
|
||||
const methods = ['loadOverviewAnalytics', 'createStatusChart', 'createFoldersChart'];
|
||||
methods.forEach(method => {
|
||||
if (typeof manager[method] === 'function') {
|
||||
testResults.push(`<div class="test-pass">✓ Method ${method} exists</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Method ${method} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
// Check for overview elements
|
||||
const overviewElements = [
|
||||
'totalBookmarksCount',
|
||||
'validLinksCount',
|
||||
'invalidLinksCount',
|
||||
'duplicatesCount',
|
||||
'statusChart',
|
||||
'foldersChart'
|
||||
];
|
||||
|
||||
overviewElements.forEach(elementId => {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
testResults.push(`<div class="test-pass">✓ Element ${elementId} found</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Element ${elementId} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testStatusChart() {
|
||||
const results = document.getElementById('overviewResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test chart drawing methods
|
||||
const chartMethods = ['drawPieChart', 'drawBarChart', 'drawLineChart', 'drawMultiLineChart'];
|
||||
chartMethods.forEach(method => {
|
||||
if (typeof manager[method] === 'function') {
|
||||
testResults.push(`<div class="test-pass">✓ Chart method ${method} exists</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Chart method ${method} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testFoldersChart() {
|
||||
const results = document.getElementById('overviewResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test folder stats method
|
||||
if (typeof manager.getFolderStats === 'function') {
|
||||
testResults.push('<div class="test-pass">✓ getFolderStats method exists</div>');
|
||||
|
||||
// Test with sample data
|
||||
if (manager.bookmarks.length > 0) {
|
||||
const folderStats = manager.getFolderStats();
|
||||
if (Object.keys(folderStats).length > 0) {
|
||||
testResults.push('<div class="test-pass">✓ getFolderStats returns data</div>');
|
||||
testResults.push(`<div class="test-info">Found ${Object.keys(folderStats).length} folders</div>`);
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ getFolderStats returns empty data</div>');
|
||||
}
|
||||
} else {
|
||||
testResults.push('<div class="test-info">No test data available for folder stats test</div>');
|
||||
}
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ getFolderStats method missing</div>');
|
||||
}
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testTrendsTab() {
|
||||
const results = document.getElementById('trendsResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test trends analytics methods
|
||||
const methods = ['loadTrendsAnalytics', 'createTrendsChart', 'createTestingTrendsChart'];
|
||||
methods.forEach(method => {
|
||||
if (typeof manager[method] === 'function') {
|
||||
testResults.push(`<div class="test-pass">✓ Method ${method} exists</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Method ${method} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
// Check for trends elements
|
||||
const trendsElements = ['trendsTimeframe', 'trendsChart', 'testingTrendsChart'];
|
||||
trendsElements.forEach(elementId => {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
testResults.push(`<div class="test-pass">✓ Element ${elementId} found</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Element ${elementId} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testTrendsChart() {
|
||||
const results = document.getElementById('trendsResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test utility methods for trends
|
||||
const utilityMethods = ['generateDateRange'];
|
||||
utilityMethods.forEach(method => {
|
||||
if (typeof manager[method] === 'function') {
|
||||
testResults.push(`<div class="test-pass">✓ Utility method ${method} exists</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Utility method ${method} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testTestingTrendsChart() {
|
||||
const results = document.getElementById('trendsResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test if bookmarks have lastTested property
|
||||
if (manager.bookmarks.length > 0) {
|
||||
const testedBookmarks = manager.bookmarks.filter(b => b.lastTested);
|
||||
if (testedBookmarks.length > 0) {
|
||||
testResults.push(`<div class="test-pass">✓ Found ${testedBookmarks.length} bookmarks with test data</div>`);
|
||||
} else {
|
||||
testResults.push('<div class="test-info">No bookmarks with test data found</div>');
|
||||
}
|
||||
} else {
|
||||
testResults.push('<div class="test-info">No test data available</div>');
|
||||
}
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testHealthTab() {
|
||||
const results = document.getElementById('healthResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test health analytics methods
|
||||
const methods = ['loadHealthAnalytics', 'calculateHealthMetrics', 'displayHealthIssues', 'displayHealthRecommendations'];
|
||||
methods.forEach(method => {
|
||||
if (typeof manager[method] === 'function') {
|
||||
testResults.push(`<div class="test-pass">✓ Method ${method} exists</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Method ${method} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
// Check for health elements
|
||||
const healthElements = ['healthScore', 'lastFullTest', 'healthIssuesList', 'healthRecommendations'];
|
||||
healthElements.forEach(elementId => {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
testResults.push(`<div class="test-pass">✓ Element ${elementId} found</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Element ${elementId} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testHealthMetrics() {
|
||||
const results = document.getElementById('healthResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
if (typeof manager.calculateHealthMetrics === 'function') {
|
||||
const healthData = manager.calculateHealthMetrics();
|
||||
|
||||
if (healthData && typeof healthData === 'object') {
|
||||
testResults.push('<div class="test-pass">✓ calculateHealthMetrics returns data</div>');
|
||||
|
||||
// Check required properties
|
||||
const requiredProps = ['score', 'lastFullTest', 'issues', 'recommendations'];
|
||||
requiredProps.forEach(prop => {
|
||||
if (healthData.hasOwnProperty(prop)) {
|
||||
testResults.push(`<div class="test-pass">✓ Health data has ${prop} property</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Health data missing ${prop} property</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
// Display sample health data
|
||||
testResults.push(`<div class="analytics-preview">
|
||||
<strong>Sample Health Data:</strong><br>
|
||||
Score: ${healthData.score}%<br>
|
||||
Last Full Test: ${healthData.lastFullTest}<br>
|
||||
Issues: ${healthData.issues ? healthData.issues.length : 0}<br>
|
||||
Recommendations: ${healthData.recommendations ? healthData.recommendations.length : 0}
|
||||
</div>`);
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ calculateHealthMetrics returns invalid data</div>');
|
||||
}
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ calculateHealthMetrics method missing</div>');
|
||||
}
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testHealthRecommendations() {
|
||||
const results = document.getElementById('healthResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
if (manager.bookmarks.length > 0) {
|
||||
const healthData = manager.calculateHealthMetrics();
|
||||
|
||||
if (healthData.issues && Array.isArray(healthData.issues)) {
|
||||
testResults.push(`<div class="test-pass">✓ Health issues detected: ${healthData.issues.length}</div>`);
|
||||
|
||||
healthData.issues.forEach((issue, index) => {
|
||||
testResults.push(`<div class="test-info">Issue ${index + 1}: ${issue.title} (${issue.count})</div>`);
|
||||
});
|
||||
}
|
||||
|
||||
if (healthData.recommendations && Array.isArray(healthData.recommendations)) {
|
||||
testResults.push(`<div class="test-pass">✓ Health recommendations generated: ${healthData.recommendations.length}</div>`);
|
||||
|
||||
healthData.recommendations.forEach((rec, index) => {
|
||||
testResults.push(`<div class="test-info">Recommendation ${index + 1}: ${rec.title}</div>`);
|
||||
});
|
||||
}
|
||||
} else {
|
||||
testResults.push('<div class="test-info">No test data available for health recommendations</div>');
|
||||
}
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testUsageTab() {
|
||||
const results = document.getElementById('usageResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test usage analytics methods
|
||||
const methods = ['loadUsageAnalytics', 'calculateUsageMetrics', 'createTopFoldersChart', 'createRatingsChart'];
|
||||
methods.forEach(method => {
|
||||
if (typeof manager[method] === 'function') {
|
||||
testResults.push(`<div class="test-pass">✓ Method ${method} exists</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Method ${method} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
// Check for usage elements
|
||||
const usageElements = ['mostActiveFolder', 'averageRating', 'mostVisited', 'topFoldersChart', 'ratingsChart'];
|
||||
usageElements.forEach(elementId => {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
testResults.push(`<div class="test-pass">✓ Element ${elementId} found</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Element ${elementId} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testUsageMetrics() {
|
||||
const results = document.getElementById('usageResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
if (typeof manager.calculateUsageMetrics === 'function') {
|
||||
const usageData = manager.calculateUsageMetrics();
|
||||
|
||||
if (usageData && typeof usageData === 'object') {
|
||||
testResults.push('<div class="test-pass">✓ calculateUsageMetrics returns data</div>');
|
||||
|
||||
// Check required properties
|
||||
const requiredProps = ['mostActiveFolder', 'averageRating', 'mostVisited'];
|
||||
requiredProps.forEach(prop => {
|
||||
if (usageData.hasOwnProperty(prop)) {
|
||||
testResults.push(`<div class="test-pass">✓ Usage data has ${prop} property</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Usage data missing ${prop} property</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
// Display sample usage data
|
||||
testResults.push(`<div class="analytics-preview">
|
||||
<strong>Sample Usage Data:</strong><br>
|
||||
Most Active Folder: ${usageData.mostActiveFolder}<br>
|
||||
Average Rating: ${usageData.averageRating}<br>
|
||||
Most Visited: ${usageData.mostVisited}
|
||||
</div>`);
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ calculateUsageMetrics returns invalid data</div>');
|
||||
}
|
||||
} else {
|
||||
testResults.push('<div class="test-fail">✗ calculateUsageMetrics method missing</div>');
|
||||
}
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testUsageCharts() {
|
||||
const results = document.getElementById('usageResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test color generation methods
|
||||
const colorMethods = ['generateFolderColor', 'generateRatingColor', 'hashString'];
|
||||
colorMethods.forEach(method => {
|
||||
if (typeof manager[method] === 'function') {
|
||||
testResults.push(`<div class="test-pass">✓ Color method ${method} exists</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Color method ${method} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testAnalyticsExport() {
|
||||
const results = document.getElementById('exportResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test export methods
|
||||
const exportMethods = ['exportAnalyticsData', 'generateAnalyticsReport'];
|
||||
exportMethods.forEach(method => {
|
||||
if (typeof manager[method] === 'function') {
|
||||
testResults.push(`<div class="test-pass">✓ Export method ${method} exists</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Export method ${method} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
// Check for export buttons
|
||||
const exportButtons = ['exportAnalyticsBtn', 'generateReportBtn'];
|
||||
exportButtons.forEach(buttonId => {
|
||||
const button = document.getElementById(buttonId);
|
||||
if (button) {
|
||||
testResults.push(`<div class="test-pass">✓ Export button ${buttonId} found</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Export button ${buttonId} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testReportGeneration() {
|
||||
const results = document.getElementById('exportResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test if we can generate analytics data structure
|
||||
if (manager.bookmarks.length > 0) {
|
||||
try {
|
||||
const analyticsData = {
|
||||
generatedAt: new Date().toISOString(),
|
||||
summary: {
|
||||
totalBookmarks: manager.bookmarks.length,
|
||||
validLinks: manager.bookmarks.filter(b => b.status === 'valid').length,
|
||||
invalidLinks: manager.bookmarks.filter(b => b.status === 'invalid').length,
|
||||
duplicates: manager.bookmarks.filter(b => b.status === 'duplicate').length,
|
||||
unknownStatus: manager.bookmarks.filter(b => b.status === 'unknown').length
|
||||
},
|
||||
folderStats: manager.getFolderStats(),
|
||||
healthMetrics: manager.calculateHealthMetrics(),
|
||||
usageMetrics: manager.calculateUsageMetrics()
|
||||
};
|
||||
|
||||
testResults.push('<div class="test-pass">✓ Analytics data structure can be generated</div>');
|
||||
testResults.push(`<div class="analytics-preview">
|
||||
<strong>Sample Analytics Data:</strong><br>
|
||||
Total Bookmarks: ${analyticsData.summary.totalBookmarks}<br>
|
||||
Valid Links: ${analyticsData.summary.validLinks}<br>
|
||||
Invalid Links: ${analyticsData.summary.invalidLinks}<br>
|
||||
Folders: ${Object.keys(analyticsData.folderStats).length}<br>
|
||||
Health Score: ${analyticsData.healthMetrics.score}%
|
||||
</div>`);
|
||||
} catch (error) {
|
||||
testResults.push(`<div class="test-fail">✗ Error generating analytics data: ${error.message}</div>`);
|
||||
}
|
||||
} else {
|
||||
testResults.push('<div class="test-info">No test data available for report generation test</div>');
|
||||
}
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
function testChartDrawing() {
|
||||
const results = document.getElementById('chartResults');
|
||||
let testResults = [];
|
||||
|
||||
const manager = initializeTestManager();
|
||||
|
||||
// Test chart drawing utility methods
|
||||
const chartUtilities = ['drawPieChart', 'drawBarChart', 'drawLineChart', 'drawMultiLineChart', 'drawLegend', 'drawEmptyChart'];
|
||||
chartUtilities.forEach(method => {
|
||||
if (typeof manager[method] === 'function') {
|
||||
testResults.push(`<div class="test-pass">✓ Chart utility ${method} exists</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Chart utility ${method} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
// Test if canvas elements exist for charts
|
||||
const canvasElements = ['statusChart', 'foldersChart', 'trendsChart', 'testingTrendsChart', 'topFoldersChart', 'ratingsChart'];
|
||||
canvasElements.forEach(canvasId => {
|
||||
const canvas = document.getElementById(canvasId);
|
||||
if (canvas && canvas.tagName === 'CANVAS') {
|
||||
testResults.push(`<div class="test-pass">✓ Canvas element ${canvasId} found</div>`);
|
||||
} else {
|
||||
testResults.push(`<div class="test-fail">✗ Canvas element ${canvasId} missing</div>`);
|
||||
}
|
||||
});
|
||||
|
||||
results.innerHTML = testResults.join('');
|
||||
}
|
||||
|
||||
// Initialize test on page load
|
||||
window.addEventListener('load', () => {
|
||||
console.log('Analytics functionality test page loaded');
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user