zotero-ts-api
Version:
A TypeScript API wrapper for Zotero
276 lines • 12.7 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
const vitest_1 = require("vitest");
const Item_1 = require("./Item");
const ZCreator_1 = require("./ZCreator");
const ZTag_1 = require("./ZTag");
(0, vitest_1.describe)('Item', () => {
let item;
let mockItemData;
(0, vitest_1.beforeEach)(() => {
mockItemData = {
key: 'TESTITEM123',
version: 1,
title: 'Test Article',
itemType: 'journalArticle',
creators: [
{ creatorType: 'author', firstName: 'John', lastName: 'Doe' }
],
tags: [
{ tag: 'science' }
],
collections: ['COLLECTION123'],
url: 'https://example.com',
abstractNote: 'Test abstract',
date: '2023-01-15',
language: 'en',
relations: {},
};
item = new Item_1.Item(mockItemData, 'test-api-key', 'user123', 'users');
});
(0, vitest_1.describe)('Constructor', () => {
(0, vitest_1.it)('should create item with valid data', () => {
(0, vitest_1.expect)(item.key).toBe('TESTITEM123');
(0, vitest_1.expect)(item.title).toBe('Test Article');
(0, vitest_1.expect)(item.itemType).toBe('journalArticle');
});
(0, vitest_1.it)('should create defensive copy of data', () => {
const originalData = { ...mockItemData };
const testItem = new Item_1.Item(mockItemData, 'api-key', 'user', 'users');
mockItemData.title = 'Modified Title';
(0, vitest_1.expect)(testItem.title).toBe(originalData.title);
});
});
(0, vitest_1.describe)('Getters', () => {
(0, vitest_1.it)('should return correct key', () => {
(0, vitest_1.expect)(item.key).toBe('TESTITEM123');
});
(0, vitest_1.it)('should return correct title', () => {
(0, vitest_1.expect)(item.title).toBe('Test Article');
});
(0, vitest_1.it)('should return correct itemType', () => {
(0, vitest_1.expect)(item.itemType).toBe('journalArticle');
});
(0, vitest_1.it)('should return correct url', () => {
(0, vitest_1.expect)(item.url).toBe('https://example.com');
});
(0, vitest_1.it)('should return correct abstractNote', () => {
(0, vitest_1.expect)(item.abstractNote).toBe('Test abstract');
});
(0, vitest_1.it)('should return correct date', () => {
(0, vitest_1.expect)(item.date).toBe('2023-01-15');
});
(0, vitest_1.it)('should return correct language', () => {
(0, vitest_1.expect)(item.language).toBe('en');
});
(0, vitest_1.it)('should return defensive copy of collections', () => {
const collections = item.collections;
collections.push('NEW_COLLECTION');
(0, vitest_1.expect)(item.collections).toEqual(['COLLECTION123']);
});
(0, vitest_1.it)('should return ZTag instances', () => {
const tags = item.tags;
(0, vitest_1.expect)(tags).toHaveLength(1);
(0, vitest_1.expect)(tags[0]).toBeInstanceOf(ZTag_1.ZTag);
(0, vitest_1.expect)(tags[0].name).toBe('science');
});
(0, vitest_1.it)('should return ZCreator instances', () => {
const creators = item.creators;
(0, vitest_1.expect)(creators).toHaveLength(1);
(0, vitest_1.expect)(creators[0]).toBeInstanceOf(ZCreator_1.ZCreator);
(0, vitest_1.expect)(creators[0].firstName).toBe('John');
(0, vitest_1.expect)(creators[0].lastName).toBe('Doe');
});
(0, vitest_1.it)('should handle undefined optional fields', () => {
const minimalData = {
key: 'MINIMAL123',
version: 1,
title: 'Minimal Item',
itemType: 'webpage',
creators: [],
tags: [],
collections: [],
relations: {},
};
const minimalItem = new Item_1.Item(minimalData, 'api-key', 'user', 'users');
(0, vitest_1.expect)(minimalItem.url).toBeUndefined();
(0, vitest_1.expect)(minimalItem.abstractNote).toBeUndefined();
(0, vitest_1.expect)(minimalItem.date).toBeUndefined();
(0, vitest_1.expect)(minimalItem.language).toBeUndefined();
});
});
(0, vitest_1.describe)('Setters', () => {
(0, vitest_1.it)('should set title correctly', () => {
item.title = 'New Title';
(0, vitest_1.expect)(item.title).toBe('New Title');
});
(0, vitest_1.it)('should trim title whitespace', () => {
item.title = ' Spaced Title ';
(0, vitest_1.expect)(item.title).toBe('Spaced Title');
});
(0, vitest_1.it)('should throw error for empty title', () => {
(0, vitest_1.expect)(() => { item.title = ''; })
.toThrow('Title cannot be empty');
});
(0, vitest_1.it)('should throw error for whitespace-only title', () => {
(0, vitest_1.expect)(() => { item.title = ' '; })
.toThrow('Title cannot be empty');
});
(0, vitest_1.it)('should throw error for null title', () => {
(0, vitest_1.expect)(() => { item.title = null; })
.toThrow('Title cannot be empty');
});
(0, vitest_1.it)('should set itemType correctly', () => {
item.itemType = 'book';
(0, vitest_1.expect)(item.itemType).toBe('book');
});
(0, vitest_1.it)('should trim itemType whitespace', () => {
item.itemType = ' webpage ';
(0, vitest_1.expect)(item.itemType).toBe('webpage');
});
(0, vitest_1.it)('should throw error for empty itemType', () => {
(0, vitest_1.expect)(() => { item.itemType = ''; })
.toThrow('Item type cannot be empty');
});
(0, vitest_1.it)('should throw error for whitespace-only itemType', () => {
(0, vitest_1.expect)(() => { item.itemType = ' '; })
.toThrow('Item type cannot be empty');
});
(0, vitest_1.it)('should set optional fields', () => {
item.url = 'https://newurl.com';
item.abstractNote = 'New abstract';
item.date = '2024-01-01';
item.language = 'fr';
(0, vitest_1.expect)(item.url).toBe('https://newurl.com');
(0, vitest_1.expect)(item.abstractNote).toBe('New abstract');
(0, vitest_1.expect)(item.date).toBe('2024-01-01');
(0, vitest_1.expect)(item.language).toBe('fr');
});
(0, vitest_1.it)('should allow undefined for optional fields', () => {
item.url = undefined;
item.abstractNote = undefined;
item.date = undefined;
item.language = undefined;
(0, vitest_1.expect)(item.url).toBeUndefined();
(0, vitest_1.expect)(item.abstractNote).toBeUndefined();
(0, vitest_1.expect)(item.date).toBeUndefined();
(0, vitest_1.expect)(item.language).toBeUndefined();
});
});
(0, vitest_1.describe)('Creator management', () => {
(0, vitest_1.it)('should add creator', () => {
const newCreator = new ZCreator_1.ZCreator({
creatorType: 'editor',
firstName: 'Jane',
lastName: 'Smith'
});
item.addCreator(newCreator);
const creators = item.creators;
(0, vitest_1.expect)(creators).toHaveLength(2);
(0, vitest_1.expect)(creators[1].firstName).toBe('Jane');
(0, vitest_1.expect)(creators[1].lastName).toBe('Smith');
});
(0, vitest_1.it)('should remove creator by valid index', () => {
item.removeCreator(0);
(0, vitest_1.expect)(item.creators).toHaveLength(0);
});
(0, vitest_1.it)('should ignore invalid removal index (negative)', () => {
const originalLength = item.creators.length;
item.removeCreator(-1);
(0, vitest_1.expect)(item.creators).toHaveLength(originalLength);
});
(0, vitest_1.it)('should ignore invalid removal index (too high)', () => {
const originalLength = item.creators.length;
item.removeCreator(999);
(0, vitest_1.expect)(item.creators).toHaveLength(originalLength);
});
});
(0, vitest_1.describe)('Tag management', () => {
(0, vitest_1.it)('should add tag', () => {
const newTag = new ZTag_1.ZTag({ tag: 'technology' });
item.addTag(newTag);
const tags = item.tags;
(0, vitest_1.expect)(tags).toHaveLength(2);
(0, vitest_1.expect)(tags[1].name).toBe('technology');
});
(0, vitest_1.it)('should remove tag by valid index', () => {
item.removeTag(0);
(0, vitest_1.expect)(item.tags).toHaveLength(0);
});
(0, vitest_1.it)('should ignore invalid tag removal index (negative)', () => {
const originalLength = item.tags.length;
item.removeTag(-1);
(0, vitest_1.expect)(item.tags).toHaveLength(originalLength);
});
(0, vitest_1.it)('should ignore invalid tag removal index (too high)', () => {
const originalLength = item.tags.length;
item.removeTag(999);
(0, vitest_1.expect)(item.tags).toHaveLength(originalLength);
});
});
(0, vitest_1.describe)('update()', () => {
(0, vitest_1.it)('should update item successfully', async () => {
vitest_1.vi.mocked(fetch).mockResolvedValueOnce({
ok: true
});
await (0, vitest_1.expect)(item.update()).resolves.toBeUndefined();
(0, vitest_1.expect)(fetch).toHaveBeenCalledWith('https://api.zotero.org/users/user123/items/TESTITEM123', {
method: 'PUT',
headers: {
'Zotero-API-Key': 'test-api-key',
'Content-Type': 'application/json'
},
body: vitest_1.expect.stringContaining('"title":"Test Article"')
});
});
(0, vitest_1.it)('should handle update failure', async () => {
vitest_1.vi.mocked(fetch).mockResolvedValueOnce({
ok: false,
statusText: 'Conflict'
});
await (0, vitest_1.expect)(item.update())
.rejects.toThrow('Failed to update item TESTITEM123: Conflict');
});
(0, vitest_1.it)('should handle network error during update', async () => {
vitest_1.vi.mocked(fetch).mockRejectedValueOnce(new Error('Network failure'));
await (0, vitest_1.expect)(item.update())
.rejects.toThrow('Network failure');
});
});
(0, vitest_1.describe)('delete()', () => {
(0, vitest_1.it)('should delete item successfully', async () => {
vitest_1.vi.mocked(fetch).mockResolvedValueOnce({
ok: true
});
await (0, vitest_1.expect)(item.delete()).resolves.toBeUndefined();
(0, vitest_1.expect)(fetch).toHaveBeenCalledWith('https://api.zotero.org/users/user123/items/TESTITEM123', {
method: 'DELETE',
headers: {
'Zotero-API-Key': 'test-api-key',
'If-Unmodified-Since-Version': '1'
}
});
});
(0, vitest_1.it)('should handle delete failure', async () => {
vitest_1.vi.mocked(fetch).mockResolvedValueOnce({
ok: false,
statusText: 'Forbidden'
});
await (0, vitest_1.expect)(item.delete())
.rejects.toThrow('Failed to delete item TESTITEM123: Forbidden');
});
});
(0, vitest_1.describe)('toJSON()', () => {
(0, vitest_1.it)('should return copy of item data', () => {
const json = item.toJSON();
(0, vitest_1.expect)(json).toEqual(mockItemData);
(0, vitest_1.expect)(json).not.toBe(mockItemData);
});
(0, vitest_1.it)('should reflect current state after modifications', () => {
item.title = 'Modified Title';
const json = item.toJSON();
(0, vitest_1.expect)(json.title).toBe('Modified Title');
});
});
});
//# sourceMappingURL=Item.test.js.map