251 lines
9.1 KiB
TypeScript
251 lines
9.1 KiB
TypeScript
|
|
/**
|
||
|
|
* Global Blocks API Service Tests
|
||
|
|
*/
|
||
|
|
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||
|
|
|
||
|
|
// Mock fetch globally
|
||
|
|
const mockFetch = vi.fn();
|
||
|
|
vi.stubGlobal('fetch', mockFetch);
|
||
|
|
|
||
|
|
// Import after mock is set up
|
||
|
|
import {
|
||
|
|
getGlobalFolders,
|
||
|
|
createGlobalFolder,
|
||
|
|
renameGlobalFolder,
|
||
|
|
deleteGlobalFolder,
|
||
|
|
getGlobalBlocks,
|
||
|
|
createGlobalBlock,
|
||
|
|
renameGlobalBlock,
|
||
|
|
deleteGlobalBlock,
|
||
|
|
} from '../src/services/api';
|
||
|
|
|
||
|
|
const TOKEN = 'test-token-123';
|
||
|
|
|
||
|
|
function mockResponse(data: unknown, ok = true, status = 200): Response {
|
||
|
|
return {
|
||
|
|
ok,
|
||
|
|
status,
|
||
|
|
json: () => Promise.resolve(data),
|
||
|
|
} as Response;
|
||
|
|
}
|
||
|
|
|
||
|
|
beforeEach(() => {
|
||
|
|
mockFetch.mockReset();
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('Global Blocks API Service', () => {
|
||
|
|
describe('getGlobalFolders', () => {
|
||
|
|
it('should fetch all folders', async () => {
|
||
|
|
const folders = [{ id: 'f1', name: 'Folder 1', parent_id: null, created_at: '2026-01-01' }];
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(folders));
|
||
|
|
const result = await getGlobalFolders(TOKEN);
|
||
|
|
expect(result).toEqual(folders);
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.stringContaining('/api/global-folders'),
|
||
|
|
expect.objectContaining({ headers: expect.objectContaining({ Authorization: `Bearer ${TOKEN}` }) }),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should fetch root folders when parentId=null', async () => {
|
||
|
|
const folders = [{ id: 'f1', name: 'Root', parent_id: null, created_at: '2026-01-01' }];
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(folders));
|
||
|
|
const result = await getGlobalFolders(TOKEN, null);
|
||
|
|
expect(result).toEqual(folders);
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.stringContaining('parentId=null'),
|
||
|
|
expect.anything(),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should fetch sub-folders for a parent', async () => {
|
||
|
|
const folders = [{ id: 'f2', name: 'Sub', parent_id: 'f1', created_at: '2026-01-01' }];
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(folders));
|
||
|
|
const result = await getGlobalFolders(TOKEN, 'f1');
|
||
|
|
expect(result).toEqual(folders);
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.stringContaining('parentId=f1'),
|
||
|
|
expect.anything(),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should throw on fetch failure', async () => {
|
||
|
|
mockFetch.mockResolvedValue(mockResponse({ error: 'fail' }, false, 500));
|
||
|
|
await expect(getGlobalFolders(TOKEN)).rejects.toThrow('Failed to load global folders');
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('createGlobalFolder', () => {
|
||
|
|
it('should create a folder', async () => {
|
||
|
|
const folder = { id: 'f1', name: 'New Folder', parent_id: null, created_at: '2026-01-01' };
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(folder, true, 201));
|
||
|
|
const result = await createGlobalFolder(TOKEN, 'New Folder');
|
||
|
|
expect(result).toEqual(folder);
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.stringContaining('/api/global-folders'),
|
||
|
|
expect.objectContaining({
|
||
|
|
method: 'POST',
|
||
|
|
body: JSON.stringify({ name: 'New Folder', parent_id: null }),
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should create a sub-folder with parentId', async () => {
|
||
|
|
const folder = { id: 'f2', name: 'Sub', parent_id: 'f1', created_at: '2026-01-01' };
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(folder, true, 201));
|
||
|
|
const result = await createGlobalFolder(TOKEN, 'Sub', 'f1');
|
||
|
|
expect(result).toEqual(folder);
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.anything(),
|
||
|
|
expect.objectContaining({
|
||
|
|
body: JSON.stringify({ name: 'Sub', parent_id: 'f1' }),
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('renameGlobalFolder', () => {
|
||
|
|
it('should rename a folder', async () => {
|
||
|
|
const folder = { id: 'f1', name: 'Renamed', parent_id: null, created_at: '2026-01-01' };
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(folder));
|
||
|
|
const result = await renameGlobalFolder(TOKEN, 'f1', 'Renamed');
|
||
|
|
expect(result).toEqual(folder);
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.stringContaining('/api/global-folders/f1'),
|
||
|
|
expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ name: 'Renamed' }) }),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('deleteGlobalFolder', () => {
|
||
|
|
it('should delete a folder', async () => {
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(null, true, 204));
|
||
|
|
await deleteGlobalFolder(TOKEN, 'f1');
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.stringContaining('/api/global-folders/f1'),
|
||
|
|
expect.objectContaining({ method: 'DELETE' }),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('getGlobalBlocks', () => {
|
||
|
|
it('should fetch all blocks', async () => {
|
||
|
|
const blocks = [{ id: 'b1', folder_id: null, name: 'Block 1', block_data: '{}', svg_data: null, created_at: '2026-01-01', updated_at: '2026-01-01' }];
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(blocks));
|
||
|
|
const result = await getGlobalBlocks(TOKEN);
|
||
|
|
expect(result).toEqual(blocks);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should fetch blocks for a folder', async () => {
|
||
|
|
const blocks = [{ id: 'b1', folder_id: 'f1', name: 'Block 1', block_data: '{}', svg_data: null, created_at: '2026-01-01', updated_at: '2026-01-01' }];
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(blocks));
|
||
|
|
const result = await getGlobalBlocks(TOKEN, 'f1');
|
||
|
|
expect(result).toEqual(blocks);
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.stringContaining('folderId=f1'),
|
||
|
|
expect.anything(),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should fetch root-level blocks with folderId=null', async () => {
|
||
|
|
const blocks = [{ id: 'b1', folder_id: null, name: 'Root Block', block_data: '{}', svg_data: null, created_at: '2026-01-01', updated_at: '2026-01-01' }];
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(blocks));
|
||
|
|
const result = await getGlobalBlocks(TOKEN, null);
|
||
|
|
expect(result).toEqual(blocks);
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.stringContaining('folderId=null'),
|
||
|
|
expect.anything(),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('createGlobalBlock', () => {
|
||
|
|
it('should create a block with full data', async () => {
|
||
|
|
const block = { id: 'b1', folder_id: 'f1', name: 'Chair', block_data: '{"type":"chair"}', svg_data: '<svg/>', created_at: '2026-01-01', updated_at: '2026-01-01' };
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(block, true, 201));
|
||
|
|
const result = await createGlobalBlock(TOKEN, {
|
||
|
|
name: 'Chair',
|
||
|
|
folder_id: 'f1',
|
||
|
|
block_data: '{"type":"chair"}',
|
||
|
|
svg_data: '<svg/>',
|
||
|
|
});
|
||
|
|
expect(result).toEqual(block);
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.anything(),
|
||
|
|
expect.objectContaining({
|
||
|
|
method: 'POST',
|
||
|
|
body: JSON.stringify({
|
||
|
|
name: 'Chair',
|
||
|
|
folder_id: 'f1',
|
||
|
|
block_data: '{"type":"chair"}',
|
||
|
|
svg_data: '<svg/>',
|
||
|
|
}),
|
||
|
|
}),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('renameGlobalBlock', () => {
|
||
|
|
it('should rename a block', async () => {
|
||
|
|
const block = { id: 'b1', folder_id: null, name: 'Renamed', block_data: '{}', svg_data: null, created_at: '2026-01-01', updated_at: '2026-01-01' };
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(block));
|
||
|
|
const result = await renameGlobalBlock(TOKEN, 'b1', 'Renamed');
|
||
|
|
expect(result).toEqual(block);
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.stringContaining('/api/global-blocks/b1'),
|
||
|
|
expect.objectContaining({ method: 'PATCH', body: JSON.stringify({ name: 'Renamed' }) }),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
describe('deleteGlobalBlock', () => {
|
||
|
|
it('should delete a block', async () => {
|
||
|
|
mockFetch.mockResolvedValue(mockResponse(null, true, 204));
|
||
|
|
await deleteGlobalBlock(TOKEN, 'b1');
|
||
|
|
expect(mockFetch).toHaveBeenCalledWith(
|
||
|
|
expect.stringContaining('/api/global-blocks/b1'),
|
||
|
|
expect.objectContaining({ method: 'DELETE' }),
|
||
|
|
);
|
||
|
|
});
|
||
|
|
});
|
||
|
|
});
|
||
|
|
|
||
|
|
/**
|
||
|
|
* GroupTool Extended Tests — getGroupElements method
|
||
|
|
*/
|
||
|
|
import { GroupManager } from '../src/tools/modification/GroupTool';
|
||
|
|
|
||
|
|
describe('GroupManager getGroupElements', () => {
|
||
|
|
it('should return only the element itself when not in a group', () => {
|
||
|
|
const gm = new GroupManager();
|
||
|
|
const result = gm.getGroupElements('el1');
|
||
|
|
expect(result).toEqual(['el1']);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should return all group members when element is in a group', () => {
|
||
|
|
const gm = new GroupManager();
|
||
|
|
gm.createGroup(['el1', 'el2', 'el3']);
|
||
|
|
const result = gm.getGroupElements('el1');
|
||
|
|
expect(result).toHaveLength(3);
|
||
|
|
expect(result).toContain('el1');
|
||
|
|
expect(result).toContain('el2');
|
||
|
|
expect(result).toContain('el3');
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should return all group members for any member element', () => {
|
||
|
|
const gm = new GroupManager();
|
||
|
|
gm.createGroup(['a', 'b', 'c']);
|
||
|
|
expect(gm.getGroupElements('b')).toContain('a');
|
||
|
|
expect(gm.getGroupElements('b')).toContain('b');
|
||
|
|
expect(gm.getGroupElements('b')).toContain('c');
|
||
|
|
expect(gm.getGroupElements('c')).toHaveLength(3);
|
||
|
|
});
|
||
|
|
|
||
|
|
it('should return only the element when group was dissolved', () => {
|
||
|
|
const gm = new GroupManager();
|
||
|
|
const group = gm.createGroup(['x', 'y']);
|
||
|
|
gm.ungroup(group.id);
|
||
|
|
expect(gm.getGroupElements('x')).toEqual(['x']);
|
||
|
|
expect(gm.getGroupElements('y')).toEqual(['y']);
|
||
|
|
});
|
||
|
|
});
|