WIP
This commit is contained in:
381
tests/test_json_import_export.html
Normal file
381
tests/test_json_import_export.html
Normal file
@ -0,0 +1,381 @@
|
||||
<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>JSON Import/Export Test</title>
|
||||
<style>
|
||||
body {
|
||||
font-family: Arial, sans-serif;
|
||||
max-width: 800px;
|
||||
margin: 0 auto;
|
||||
padding: 20px;
|
||||
}
|
||||
.test-section {
|
||||
margin: 20px 0;
|
||||
padding: 15px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 5px;
|
||||
}
|
||||
.test-result {
|
||||
margin: 10px 0;
|
||||
padding: 10px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
.test-pass {
|
||||
background-color: #d4edda;
|
||||
color: #155724;
|
||||
border: 1px solid #c3e6cb;
|
||||
}
|
||||
.test-fail {
|
||||
background-color: #f8d7da;
|
||||
color: #721c24;
|
||||
border: 1px solid #f5c6cb;
|
||||
}
|
||||
button {
|
||||
margin: 5px;
|
||||
padding: 8px 16px;
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
}
|
||||
button:hover {
|
||||
background: #0056b3;
|
||||
}
|
||||
pre {
|
||||
background: #f8f9fa;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
font-size: 12px;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<h1>JSON Import/Export Test</h1>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Test JSON Export Format</h2>
|
||||
<button onclick="testJSONExport()">Test JSON Export</button>
|
||||
<div id="exportResults"></div>
|
||||
<pre id="exportSample"></pre>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Test JSON Import Parsing</h2>
|
||||
<button onclick="testJSONImport()">Test JSON Import</button>
|
||||
<div id="importResults"></div>
|
||||
</div>
|
||||
|
||||
<div class="test-section">
|
||||
<h2>Test Round-trip (Export → Import)</h2>
|
||||
<button onclick="testRoundTrip()">Test Round-trip</button>
|
||||
<div id="roundtripResults"></div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
// Mock BookmarkManager for testing
|
||||
class MockBookmarkManager {
|
||||
constructor() {
|
||||
this.bookmarks = [
|
||||
{
|
||||
id: '1',
|
||||
title: 'Test Bookmark 1',
|
||||
url: 'https://example.com',
|
||||
folder: 'Test Folder',
|
||||
tags: ['test', 'example'],
|
||||
notes: 'Test notes',
|
||||
rating: 4,
|
||||
favorite: true,
|
||||
addDate: Date.now(),
|
||||
lastModified: null,
|
||||
lastVisited: null,
|
||||
icon: '',
|
||||
status: 'valid',
|
||||
errorCategory: null,
|
||||
lastTested: null
|
||||
},
|
||||
{
|
||||
id: '2',
|
||||
title: 'Test Bookmark 2',
|
||||
url: 'https://test.com',
|
||||
folder: '',
|
||||
tags: [],
|
||||
notes: '',
|
||||
rating: 0,
|
||||
favorite: false,
|
||||
addDate: Date.now() - 86400000,
|
||||
lastModified: Date.now(),
|
||||
lastVisited: Date.now() - 3600000,
|
||||
icon: 'https://test.com/favicon.ico',
|
||||
status: 'unknown',
|
||||
errorCategory: null,
|
||||
lastTested: null
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// Generate JSON export (matching the actual implementation)
|
||||
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,
|
||||
errorCategory: bookmark.errorCategory,
|
||||
lastTested: bookmark.lastTested
|
||||
}))
|
||||
};
|
||||
|
||||
return JSON.stringify(exportData, null, 2);
|
||||
}
|
||||
|
||||
// Parse JSON bookmarks (matching the actual implementation)
|
||||
parseJSONBookmarks(content) {
|
||||
try {
|
||||
const data = JSON.parse(content);
|
||||
|
||||
// Check if it's our export format
|
||||
if (data.bookmarks && Array.isArray(data.bookmarks)) {
|
||||
return data.bookmarks.map(bookmark => ({
|
||||
id: bookmark.id || Date.now() + Math.random(),
|
||||
title: bookmark.title || 'Untitled',
|
||||
url: bookmark.url || '',
|
||||
folder: bookmark.folder || '',
|
||||
tags: bookmark.tags || [],
|
||||
notes: bookmark.notes || '',
|
||||
rating: bookmark.rating || 0,
|
||||
favorite: bookmark.favorite || false,
|
||||
addDate: bookmark.addDate || Date.now(),
|
||||
lastModified: bookmark.lastModified || null,
|
||||
lastVisited: bookmark.lastVisited || null,
|
||||
icon: bookmark.icon || '',
|
||||
status: 'unknown', // Reset status on import
|
||||
errorCategory: null,
|
||||
lastTested: null
|
||||
}));
|
||||
}
|
||||
|
||||
// If it's just an array of bookmarks
|
||||
if (Array.isArray(data)) {
|
||||
return data.map(bookmark => ({
|
||||
id: bookmark.id || Date.now() + Math.random(),
|
||||
title: bookmark.title || 'Untitled',
|
||||
url: bookmark.url || '',
|
||||
folder: bookmark.folder || '',
|
||||
tags: bookmark.tags || [],
|
||||
notes: bookmark.notes || '',
|
||||
rating: bookmark.rating || 0,
|
||||
favorite: bookmark.favorite || false,
|
||||
addDate: bookmark.addDate || Date.now(),
|
||||
lastModified: bookmark.lastModified || null,
|
||||
lastVisited: bookmark.lastVisited || null,
|
||||
icon: bookmark.icon || '',
|
||||
status: 'unknown',
|
||||
errorCategory: null,
|
||||
lastTested: null
|
||||
}));
|
||||
}
|
||||
|
||||
throw new Error('Invalid JSON bookmark format');
|
||||
} catch (error) {
|
||||
throw new Error(`Failed to parse JSON bookmarks: ${error.message}`);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const mockManager = new MockBookmarkManager();
|
||||
|
||||
function displayResult(containerId, message, isPass) {
|
||||
const container = document.getElementById(containerId);
|
||||
const resultDiv = document.createElement('div');
|
||||
resultDiv.className = `test-result ${isPass ? 'test-pass' : 'test-fail'}`;
|
||||
resultDiv.textContent = message;
|
||||
container.appendChild(resultDiv);
|
||||
}
|
||||
|
||||
function testJSONExport() {
|
||||
const container = document.getElementById('exportResults');
|
||||
const sampleContainer = document.getElementById('exportSample');
|
||||
container.innerHTML = '';
|
||||
sampleContainer.innerHTML = '';
|
||||
|
||||
try {
|
||||
const jsonExport = mockManager.generateJSONExport(mockManager.bookmarks);
|
||||
const exportData = JSON.parse(jsonExport);
|
||||
|
||||
// Test export structure
|
||||
const hasExportDate = exportData.exportDate && typeof exportData.exportDate === 'string';
|
||||
displayResult('exportResults', `Export date present: ${hasExportDate ? 'PASS' : 'FAIL'}`, hasExportDate);
|
||||
|
||||
const hasVersion = exportData.version && exportData.version === '1.1';
|
||||
displayResult('exportResults', `Version correct: ${hasVersion ? 'PASS' : 'FAIL'}`, hasVersion);
|
||||
|
||||
const hasCorrectCount = exportData.totalBookmarks === mockManager.bookmarks.length;
|
||||
displayResult('exportResults', `Bookmark count correct: ${hasCorrectCount ? 'PASS' : 'FAIL'}`, hasCorrectCount);
|
||||
|
||||
const hasBookmarksArray = Array.isArray(exportData.bookmarks);
|
||||
displayResult('exportResults', `Bookmarks array present: ${hasBookmarksArray ? 'PASS' : 'FAIL'}`, hasBookmarksArray);
|
||||
|
||||
// Test first bookmark structure
|
||||
if (exportData.bookmarks.length > 0) {
|
||||
const firstBookmark = exportData.bookmarks[0];
|
||||
const hasRequiredFields = firstBookmark.id && firstBookmark.title && firstBookmark.url;
|
||||
displayResult('exportResults', `Required fields present: ${hasRequiredFields ? 'PASS' : 'FAIL'}`, hasRequiredFields);
|
||||
|
||||
const hasOptionalFields = 'tags' in firstBookmark && 'notes' in firstBookmark && 'rating' in firstBookmark;
|
||||
displayResult('exportResults', `Optional fields present: ${hasOptionalFields ? 'PASS' : 'FAIL'}`, hasOptionalFields);
|
||||
}
|
||||
|
||||
// Show sample export
|
||||
sampleContainer.textContent = jsonExport;
|
||||
|
||||
} catch (error) {
|
||||
displayResult('exportResults', `Export error: ${error.message}`, false);
|
||||
}
|
||||
}
|
||||
|
||||
function testJSONImport() {
|
||||
const container = document.getElementById('importResults');
|
||||
container.innerHTML = '';
|
||||
|
||||
try {
|
||||
// Test with our export format
|
||||
const sampleExport = {
|
||||
exportDate: new Date().toISOString(),
|
||||
version: '1.1',
|
||||
totalBookmarks: 2,
|
||||
bookmarks: [
|
||||
{
|
||||
id: 'test1',
|
||||
title: 'Import Test 1',
|
||||
url: 'https://import-test.com',
|
||||
folder: 'Import Folder',
|
||||
tags: ['import', 'test'],
|
||||
notes: 'Import test notes',
|
||||
rating: 3,
|
||||
favorite: false,
|
||||
addDate: Date.now(),
|
||||
lastModified: null,
|
||||
lastVisited: null,
|
||||
icon: '',
|
||||
status: 'valid',
|
||||
errorCategory: null,
|
||||
lastTested: null
|
||||
},
|
||||
{
|
||||
id: 'test2',
|
||||
title: 'Import Test 2',
|
||||
url: 'https://import-test2.com',
|
||||
folder: '',
|
||||
tags: [],
|
||||
notes: '',
|
||||
rating: 0,
|
||||
favorite: true,
|
||||
addDate: Date.now(),
|
||||
lastModified: null,
|
||||
lastVisited: null,
|
||||
icon: '',
|
||||
status: 'unknown',
|
||||
errorCategory: null,
|
||||
lastTested: null
|
||||
}
|
||||
]
|
||||
};
|
||||
|
||||
const importedBookmarks = mockManager.parseJSONBookmarks(JSON.stringify(sampleExport));
|
||||
|
||||
const correctCount = importedBookmarks.length === 2;
|
||||
displayResult('importResults', `Import count correct: ${correctCount ? 'PASS' : 'FAIL'}`, correctCount);
|
||||
|
||||
const firstBookmarkCorrect = importedBookmarks[0].title === 'Import Test 1' &&
|
||||
importedBookmarks[0].url === 'https://import-test.com';
|
||||
displayResult('importResults', `First bookmark correct: ${firstBookmarkCorrect ? 'PASS' : 'FAIL'}`, firstBookmarkCorrect);
|
||||
|
||||
const statusReset = importedBookmarks[0].status === 'unknown';
|
||||
displayResult('importResults', `Status reset on import: ${statusReset ? 'PASS' : 'FAIL'}`, statusReset);
|
||||
|
||||
const tagsPreserved = Array.isArray(importedBookmarks[0].tags) &&
|
||||
importedBookmarks[0].tags.includes('import');
|
||||
displayResult('importResults', `Tags preserved: ${tagsPreserved ? 'PASS' : 'FAIL'}`, tagsPreserved);
|
||||
|
||||
// Test with simple array format
|
||||
const simpleArray = [
|
||||
{ title: 'Simple Test', url: 'https://simple.com', folder: 'Simple' }
|
||||
];
|
||||
const simpleImported = mockManager.parseJSONBookmarks(JSON.stringify(simpleArray));
|
||||
const simpleWorking = simpleImported.length === 1 && simpleImported[0].title === 'Simple Test';
|
||||
displayResult('importResults', `Simple array format: ${simpleWorking ? 'PASS' : 'FAIL'}`, simpleWorking);
|
||||
|
||||
} catch (error) {
|
||||
displayResult('importResults', `Import error: ${error.message}`, false);
|
||||
}
|
||||
}
|
||||
|
||||
function testRoundTrip() {
|
||||
const container = document.getElementById('roundtripResults');
|
||||
container.innerHTML = '';
|
||||
|
||||
try {
|
||||
// Export bookmarks
|
||||
const exported = mockManager.generateJSONExport(mockManager.bookmarks);
|
||||
|
||||
// Import them back
|
||||
const imported = mockManager.parseJSONBookmarks(exported);
|
||||
|
||||
// Compare
|
||||
const countMatch = imported.length === mockManager.bookmarks.length;
|
||||
displayResult('roundtripResults', `Count preserved: ${countMatch ? 'PASS' : 'FAIL'}`, countMatch);
|
||||
|
||||
const titlesMatch = imported.every((bookmark, index) =>
|
||||
bookmark.title === mockManager.bookmarks[index].title
|
||||
);
|
||||
displayResult('roundtripResults', `Titles preserved: ${titlesMatch ? 'PASS' : 'FAIL'}`, titlesMatch);
|
||||
|
||||
const urlsMatch = imported.every((bookmark, index) =>
|
||||
bookmark.url === mockManager.bookmarks[index].url
|
||||
);
|
||||
displayResult('roundtripResults', `URLs preserved: ${urlsMatch ? 'PASS' : 'FAIL'}`, urlsMatch);
|
||||
|
||||
const foldersMatch = imported.every((bookmark, index) =>
|
||||
bookmark.folder === mockManager.bookmarks[index].folder
|
||||
);
|
||||
displayResult('roundtripResults', `Folders preserved: ${foldersMatch ? 'PASS' : 'FAIL'}`, foldersMatch);
|
||||
|
||||
const tagsMatch = imported.every((bookmark, index) =>
|
||||
JSON.stringify(bookmark.tags) === JSON.stringify(mockManager.bookmarks[index].tags)
|
||||
);
|
||||
displayResult('roundtripResults', `Tags preserved: ${tagsMatch ? 'PASS' : 'FAIL'}`, tagsMatch);
|
||||
|
||||
const ratingsMatch = imported.every((bookmark, index) =>
|
||||
bookmark.rating === mockManager.bookmarks[index].rating
|
||||
);
|
||||
displayResult('roundtripResults', `Ratings preserved: ${ratingsMatch ? 'PASS' : 'FAIL'}`, ratingsMatch);
|
||||
|
||||
const favoritesMatch = imported.every((bookmark, index) =>
|
||||
bookmark.favorite === mockManager.bookmarks[index].favorite
|
||||
);
|
||||
displayResult('roundtripResults', `Favorites preserved: ${favoritesMatch ? 'PASS' : 'FAIL'}`, favoritesMatch);
|
||||
|
||||
} catch (error) {
|
||||
displayResult('roundtripResults', `Round-trip error: ${error.message}`, false);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user