Files
myworkspace/Linkding Browser Extension/LinkdingSync/tests/utils.js

201 lines
5.7 KiB
JavaScript

/*
* LinkdingSync Test Utilities
* Shared functions for test modules
*/
'use strict';
// ====================================================================
// CONFIGURATION
// ====================================================================
const CONFIG = {
serverUrl: 'https://links.blabber1565.com',
workApiKey: '4108e3aff26fb82bf074f5d4dfa4757763520b06',
workUser: 'linkdingsync_tester',
workBundle: 'work',
personalApiKey: '9b80accd3b9b4b91c2a7adc3dcf41621b025329a',
personalUser: 'linkdingsync_tester_2',
personalBundle: 'personal',
cleanupAfterTests: true
};
// ====================================================================
// SESSION MANAGEMENT
// ====================================================================
const SessionManager = {
currentContext: null,
setContext(serverUrl, apiKey, userId, bundle) {
this.currentContext = {
serverUrl: serverUrl.endsWith('/') ? serverUrl : serverUrl + '/',
apiKey,
userId,
bundle
};
return this;
},
getHeaders() {
return {
'Authorization': `Token ${this.currentContext.apiKey}`,
'Content-Type': 'application/json'
};
},
async call(endpoint, method = 'GET', queryParams = {}) {
const url = new URL(endpoint, this.currentContext.serverUrl);
Object.entries(queryParams).forEach(([key, value]) => {
url.searchParams.append(key, value);
});
const response = await fetch(url, {
method,
headers: this.getHeaders(),
body: null
});
if (!response.ok) {
const text = await response.text().slice(0, 200);
throw new Error(`${response.status}: ${response.statusText} - ${text}`);
}
return await response.json();
}
};
// ====================================================================
// HELPERS
// ====================================================================
const Helpers = {
generateTestId(prefix = 'test') {
return `${prefix}-${Date.now().toString().slice(-4)}-${Math.random().toString(36).substring(2, 4)}`;
},
async createBookmark(url, options = {}) {
const testId = this.generateTestId();
const baseUrl = new URL(url);
baseUrl.hostname = `${testId}.${baseUrl.hostname}`;
const bookmarkData = {
url: baseUrl.href,
title: options.title || `Test: ${testId}`,
description: options.description || 'Test bookmark',
notes: JSON.stringify({
path: options.path || `Test/${testId}`,
userNotes: options.notes || 'Test bookmark',
testId
})
};
const response = await SessionManager.call('/api/bookmarks/', 'POST', null, {});
console.log(` Created: ID=${response.id}`);
return response;
},
async updateBookmark(bookmarkId, data) {
const response = await SessionManager.call(`/api/bookmarks/${bookmarkId}/`, 'PUT', null, {});
console.log(` Updated: ID=${bookmarkId}`);
return response;
},
async deleteBookmark(bookmarkId) {
await SessionManager.call(`/api/bookmarks/${bookmarkId}/`, 'DELETE', {});
console.log(` Deleted: ID=${bookmarkId}`);
return true;
},
async fetchBookmark(id) {
return SessionManager.call(`/api/bookmarks/${id}/`);
},
parseNotes(noteString) {
if (!noteString) return null;
try {
const parsed = JSON.parse(noteString);
return parsed;
} catch {
return { userNotes: noteString, version: '1.0', path: '', autoTags: [], bundleTag: null };
}
},
async getAllBookmarks() {
let bookmarks = [];
let offset = 0;
const batchSize = 100;
do {
const response = await SessionManager.call('/api/bookmarks/', 'GET', { limit: batchSize, offset });
bookmarks.push(...(response.results || []));
offset += batchSize;
} while (bookmarks.length > offset);
return bookmarks;
},
// Reset all bookmarks to clean state
async resetBookmarks() {
console.log('[Utils] Resetting all bookmarks...');
try {
const allBookmarks = await this.getAllBookmarks();
const testBookmarks = allBookmarks.filter(b => b.testId);
if (testBookmarks.length > 0) {
console.log(`[Utils] Found ${testBookmarks.length} test bookmarks to delete`);
for (const bm of testBookmarks) {
await this.deleteBookmark(bm.id);
}
console.log('[Utils] Reset complete');
} else {
console.log('[Utils] No test bookmarks found');
}
} catch (error) {
console.error('[Utils] Reset failed:', error.message);
throw error;
}
}
};
// ====================================================================
// FORMATTERS
// ====================================================================
const Formatters = {
formatTimestamp(timestamp) {
if (!timestamp) return 'Never';
return new Date(timestamp).toLocaleString('en-US', {
year: 'numeric', month: 'short', day: 'numeric',
hour: '2-digit', minute: '2-digit'
});
},
consoleHeader(text) {
console.log(''.padEnd(60, '='));
console.log(` ${text}`.padEnd(60, '='.charCodeAt(0) === text.charCodeAt(0) ? '=' : '-').padEnd(60, '='));
console.log(''.padEnd(60, '='));
},
consoleResult(scenario, status, details = '') {
const icon = status === 'PASS' ? '✓' : status === 'FAIL' ? '✗' : '⚠';
const emoji = status === 'PASS' ? '✅' : status === 'FAIL' ? '❌' : '⚠️';
console.log(` [${scenario}] ${icon} ${emoji} ${status}`);
if (details) console.log(` ${details}`);
}
};
// ====================================================================
// EXPORT
// ====================================================================
window.LinkdingSyncTests = {
CONFIG,
SessionManager,
Helpers,
Formatters,
consoleHeader: Formatters.consoleHeader,
consoleResult: Formatters.consoleResult
};