UNPKG

zotero-ts-api

Version:
346 lines 16.6 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const vitest_1 = require("vitest"); const Library_1 = require("./Library"); const Item_1 = require("./Item"); const ZCollection_1 = require("./ZCollection"); const test_helpers_1 = require("../__tests__/utils/test-helpers"); (0, vitest_1.describe)('Library', () => { let library; const validApiKey = 'test-api-key-123'; const validUserId = 'user123'; const validGroupId = 'group456'; (0, vitest_1.beforeEach)(() => { library = new Library_1.Library(validApiKey, validUserId, 'users'); }); (0, vitest_1.describe)('Constructor', () => { (0, vitest_1.it)('should create library with valid parameters', () => { (0, vitest_1.expect)(library.apiKeyValue).toBe(validApiKey); }); (0, vitest_1.it)('should create user library', () => { const userLibrary = new Library_1.Library(validApiKey, validUserId, 'users'); (0, vitest_1.expect)(userLibrary.apiKeyValue).toBe(validApiKey); }); (0, vitest_1.it)('should create group library', () => { const groupLibrary = new Library_1.Library(validApiKey, validGroupId, 'groups'); (0, vitest_1.expect)(groupLibrary.apiKeyValue).toBe(validApiKey); }); (0, vitest_1.it)('should throw error with empty API key', () => { (0, vitest_1.expect)(() => new Library_1.Library('', validUserId, 'users')) .toThrow('API key is required'); }); (0, vitest_1.it)('should throw error with whitespace API key', () => { (0, vitest_1.expect)(() => new Library_1.Library(' ', validUserId, 'users')) .toThrow('API key is required'); }); (0, vitest_1.it)('should throw error with null API key', () => { (0, vitest_1.expect)(() => new Library_1.Library(null, validUserId, 'users')) .toThrow('API key is required'); }); (0, vitest_1.it)('should throw error with undefined API key', () => { (0, vitest_1.expect)(() => new Library_1.Library(undefined, validUserId, 'users')) .toThrow('API key is required'); }); (0, vitest_1.it)('should throw error with empty library ID', () => { (0, vitest_1.expect)(() => new Library_1.Library(validApiKey, '', 'users')) .toThrow('Library ID is required'); }); (0, vitest_1.it)('should throw error with whitespace library ID', () => { (0, vitest_1.expect)(() => new Library_1.Library(validApiKey, ' ', 'users')) .toThrow('Library ID is required'); }); }); (0, vitest_1.describe)('Getters before connection', () => { (0, vitest_1.it)('should return undefined for name before connection', () => { (0, vitest_1.expect)(library.name).toBeUndefined(); }); (0, vitest_1.it)('should return undefined for id before connection', () => { (0, vitest_1.expect)(library.id).toBeUndefined(); }); (0, vitest_1.it)('should return undefined for type before connection', () => { (0, vitest_1.expect)(library.type).toBeUndefined(); }); }); (0, vitest_1.describe)('connect()', () => { (0, vitest_1.it)('should connect successfully with valid credentials', async () => { const mockLibraryData = { id: 123, name: 'Test Library', type: 'user', description: 'Test description' }; (0, test_helpers_1.mockFetchSuccess)(mockLibraryData); await library.connect(); (0, vitest_1.expect)(library.name).toBe('Test Library'); (0, vitest_1.expect)(library.id).toBe(123); (0, vitest_1.expect)(library.type).toBe('user'); }); (0, vitest_1.it)('should handle 403 Forbidden error', async () => { (0, test_helpers_1.mockFetchError)(403, 'Forbidden'); await (0, vitest_1.expect)(library.connect()) .rejects.toThrow('Failed to connect to Zotero API (403): Forbidden'); }); (0, vitest_1.it)('should handle malformed JSON response', async () => { vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: true, json: vitest_1.vi.fn().mockRejectedValue(new Error('Invalid JSON')) }); await (0, vitest_1.expect)(library.connect()) .rejects.toThrow('Connection failed'); }); }); (0, vitest_1.describe)('getCollections()', () => { (0, vitest_1.it)('should retrieve collections successfully', async () => { const mockCollections = [ { key: 'ABC123', name: 'Collection 1', parentCollection: undefined, version: 1 }, { key: 'DEF456', name: 'Collection 2', parentCollection: 'ABC123', version: 1 } ]; vitest_1.vi.mocked(fetch).mockResolvedValueOnce((0, test_helpers_1.createMockResponse)(mockCollections)); const collections = await library.getCollections(); (0, vitest_1.expect)(collections).toHaveLength(2); (0, vitest_1.expect)(collections[0]).toBeInstanceOf(ZCollection_1.ZCollection); (0, vitest_1.expect)(collections[0].key).toBe('ABC123'); (0, vitest_1.expect)(collections[1].key).toBe('DEF456'); }); (0, vitest_1.it)('should handle empty collections response', async () => { vitest_1.vi.mocked(fetch).mockResolvedValueOnce((0, test_helpers_1.createMockResponse)([])); const collections = await library.getCollections(); (0, vitest_1.expect)(collections).toEqual([]); }); (0, vitest_1.it)('should handle API error when fetching collections', async () => { (0, test_helpers_1.mockFetchError)(500, 'Internal Server Error'); await (0, vitest_1.expect)(library.getCollections()) .rejects.toThrow('API request failed (500): Internal Server Error'); }); }); (0, vitest_1.describe)('getAllItems()', () => { (0, vitest_1.it)('should retrieve items successfully', async () => { const mockItems = [ { data: { key: 'ITEM123', title: 'Test Article', itemType: 'journalArticle', creators: [], tags: [], collections: [], version: 1, relations: {} } }, { data: { key: 'ITEM456', title: 'Another Article', itemType: 'webpage', creators: [], tags: [], collections: [], version: 2, relations: {} } } ]; vitest_1.vi.mocked(fetch).mockResolvedValueOnce((0, test_helpers_1.createMockResponse)(mockItems)); const items = await library.getAllItems(); (0, vitest_1.expect)(items).toHaveLength(2); (0, vitest_1.expect)(items[0]).toBeInstanceOf(Item_1.Item); (0, vitest_1.expect)(items[0].key).toBe('ITEM123'); (0, vitest_1.expect)(items[0].title).toBe('Test Article'); (0, vitest_1.expect)(items[1].title).toBe('Another Article'); }); (0, vitest_1.it)('should handle empty items response', async () => { vitest_1.vi.mocked(fetch).mockResolvedValueOnce((0, test_helpers_1.createMockResponse)([])); const items = await library.getAllItems(); (0, vitest_1.expect)(items).toEqual([]); }); }); (0, vitest_1.describe)('createCollection()', () => { (0, vitest_1.it)('should create collection with valid name', async () => { const mockResponse = { key: 'NEW123', name: 'New Collection', parentCollection: undefined }; vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: true, json: async () => [mockResponse] }); const collection = await library.createCollection('New Collection'); (0, vitest_1.expect)(collection).toBeInstanceOf(ZCollection_1.ZCollection); (0, vitest_1.expect)(collection.name).toBe('New Collection'); (0, vitest_1.expect)(fetch).toHaveBeenCalledWith('https://api.zotero.org/users/user123/collections', vitest_1.expect.objectContaining({ method: 'POST', body: JSON.stringify([{ name: 'New Collection', parentCollection: undefined }]) })); }); (0, vitest_1.it)('should create collection with parent', async () => { const mockResponse = { key: 'CHILD123', name: 'Child Collection', parentCollection: 'PARENT123' }; vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: true, json: async () => [mockResponse] }); const collection = await library.createCollection('Child Collection', 'PARENT123'); (0, vitest_1.expect)(collection.name).toBe('Child Collection'); (0, vitest_1.expect)(collection.parentCollection).toBe('PARENT123'); }); (0, vitest_1.it)('should throw error with empty collection name', async () => { await (0, vitest_1.expect)(library.createCollection('')) .rejects.toThrow('Collection name is required'); }); (0, vitest_1.it)('should throw error with whitespace collection name', async () => { await (0, vitest_1.expect)(library.createCollection(' ')) .rejects.toThrow('Collection name is required'); }); (0, vitest_1.it)('should throw error with null collection name', async () => { await (0, vitest_1.expect)(library.createCollection(null)) .rejects.toThrow('Collection name is required'); }); (0, vitest_1.it)('should handle API error during collection creation', async () => { vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: false, status: 400, statusText: 'Bad Request' }); await (0, vitest_1.expect)(library.createCollection('Test Collection')) .rejects.toThrow('API request failed (400): Bad Request'); }); }); (0, vitest_1.describe)('createItem()', () => { (0, vitest_1.it)('should create item with valid data', async () => { const itemData = { title: 'Test Article', itemType: 'journalArticle', url: 'https://example.com' }; const mockResponse = { successful: { '0': { data: { ...itemData, key: 'NEWITEM123', version: 1, creators: [], tags: [], collections: [] } } } }; vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: true, json: async () => mockResponse }); const item = await library.createItem(itemData); (0, vitest_1.expect)(item).toBeInstanceOf(Item_1.Item); (0, vitest_1.expect)(item.title).toBe('Test Article'); (0, vitest_1.expect)(item.key).toBe('NEWITEM123'); }); (0, vitest_1.it)('should create item with default itemType when not provided', async () => { const itemData = { title: 'Test Item' }; const mockResponse = [{ data: { title: 'Test Item', itemType: 'webpage', key: 'NEWITEM456', version: 1, creators: [], tags: [], collections: [] } }]; vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: true, json: async () => mockResponse }); const item = await library.createItem(itemData); (0, vitest_1.expect)(item.itemType).toBe('webpage'); }); (0, vitest_1.it)('should filter unknown fields and warn', async () => { const itemData = { title: 'Test Article', unknownField: 'should be filtered', anotherUnknown: 123 }; const mockResponse = [{ data: { title: 'Test Article', itemType: 'webpage', key: 'FILTERED123', version: 1, creators: [], tags: [], collections: [] } }]; vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: true, json: async () => mockResponse }); const consoleSpy = vitest_1.vi.spyOn(console, 'warn'); await library.createItem(itemData); (0, vitest_1.expect)(consoleSpy).toHaveBeenCalledWith('[Zotero] Unknown fields ignored: unknownField, anotherUnknown'); }); (0, vitest_1.it)('should throw error when title is missing', async () => { const itemData = { itemType: 'journalArticle' }; await (0, vitest_1.expect)(library.createItem(itemData)) .rejects.toThrow('A title is required to create a Zotero item'); }); (0, vitest_1.it)('should throw error when title is empty', async () => { const itemData = { title: '', itemType: 'journalArticle' }; await (0, vitest_1.expect)(library.createItem(itemData)) .rejects.toThrow('A title is required to create a Zotero item'); }); (0, vitest_1.it)('should throw error when title is whitespace', async () => { const itemData = { title: ' ', itemType: 'journalArticle' }; await (0, vitest_1.expect)(library.createItem(itemData)) .rejects.toThrow('A title is required to create a Zotero item'); }); (0, vitest_1.it)('should handle malformed API response', async () => { const itemData = { title: 'Test Article' }; vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: true, json: async () => ({ unexpected: 'format' }) }); await (0, vitest_1.expect)(library.createItem(itemData)) .rejects.toThrow('Zotero API did not return a valid created item'); }); }); (0, vitest_1.describe)('getTags()', () => { (0, vitest_1.it)('should retrieve tags successfully', async () => { const mockTags = [ { tag: 'science' }, { tag: 'research' }, { tag: 'typescript' } ]; vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: true, json: async () => mockTags }); const tags = await library.getTags(); (0, vitest_1.expect)(tags).toEqual(['science', 'research', 'typescript']); }); (0, vitest_1.it)('should handle empty tags response', async () => { vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: true, json: async () => [] }); const tags = await library.getTags(); (0, vitest_1.expect)(tags).toEqual([]); }); (0, vitest_1.it)('should handle API error when fetching tags', async () => { vitest_1.vi.mocked(fetch).mockResolvedValueOnce({ ok: false, status: 503, statusText: 'Service Unavailable' }); await (0, vitest_1.expect)(library.getTags()) .rejects.toThrow('API request failed (503): Service Unavailable'); }); }); }); //# sourceMappingURL=Library.test.js.map