Files
webref/frontend/tests/canvas/groups.test.ts
Danilo Reyes 948fe591dc
All checks were successful
CI/CD Pipeline / VM Test - security (push) Successful in 7s
CI/CD Pipeline / Backend Linting (push) Successful in 4s
CI/CD Pipeline / VM Test - backend-integration (push) Successful in 11s
CI/CD Pipeline / VM Test - full-stack (push) Successful in 8s
CI/CD Pipeline / VM Test - performance (push) Successful in 8s
CI/CD Pipeline / Nix Flake Check (push) Successful in 38s
CI/CD Pipeline / CI Summary (push) Successful in 0s
CI/CD Pipeline / Frontend Linting (push) Successful in 17s
phase 13
2025-11-02 14:48:03 -06:00

220 lines
5.4 KiB
TypeScript

/**
* Tests for grouping operations
* Tests group creation, moving groups, ungrouping
*/
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { get } from 'svelte/store';
import type { Group } from '$lib/api/groups';
import {
createGroupFromSelection,
canCreateGroup,
getGroupColorSuggestions,
generateDefaultGroupName,
} from '$lib/canvas/operations/group';
import { ungroupImages, removeImagesFromGroup } from '$lib/canvas/operations/ungroup';
import { groups, groupsLoading, groupsError, groupCount } from '$lib/stores/groups';
// Mock API
vi.mock('$lib/api/groups', () => ({
createGroup: vi.fn().mockResolvedValue({
id: 'group-1',
board_id: 'board-1',
name: 'Test Group',
color: '#FF5733',
annotation: 'Test',
member_count: 2,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
}),
listGroups: vi.fn().mockResolvedValue([]),
updateGroup: vi.fn().mockResolvedValue({
id: 'group-1',
name: 'Updated',
color: '#00FF00',
}),
deleteGroup: vi.fn().mockResolvedValue(undefined),
}));
describe('Group Creation', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('can create group from selection', () => {
const selectedIds = ['img1', 'img2'];
expect(canCreateGroup(selectedIds)).toBe(true);
});
it('cannot create group with no selection', () => {
expect(canCreateGroup([])).toBe(false);
});
it('creates group from selection', async () => {
const selectedIds = ['img1', 'img2'];
const group = await createGroupFromSelection(selectedIds, 'board-1', {
name: 'Test Group',
color: '#FF5733',
annotation: 'Test annotation',
});
expect(group).not.toBeNull();
expect(group?.name).toBe('Test Group');
});
it('calls callback on group creation', async () => {
const callback = vi.fn();
await createGroupFromSelection(['img1', 'img2'], 'board-1', {
name: 'Test Group',
color: '#FF5733',
onGroupCreate: callback,
});
expect(callback).toHaveBeenCalled();
});
it('generates default group names', () => {
const existingGroups: Group[] = [
{
id: '1',
board_id: 'board-1',
name: 'Group 1',
color: '#FF5733',
annotation: null,
member_count: 2,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
},
{
id: '2',
board_id: 'board-1',
name: 'Group 2',
color: '#FF5733',
annotation: null,
member_count: 3,
created_at: new Date().toISOString(),
updated_at: new Date().toISOString(),
},
];
const newName = generateDefaultGroupName(existingGroups);
expect(newName).toBe('Group 3');
});
it('provides color suggestions', () => {
const colors = getGroupColorSuggestions();
expect(colors).toBeInstanceOf(Array);
expect(colors.length).toBeGreaterThan(0);
expect(colors[0]).toMatch(/^#[0-9A-Fa-f]{6}$/);
});
});
describe('Groups Store', () => {
beforeEach(() => {
groups.clear();
vi.clearAllMocks();
});
it('starts with empty state', () => {
const state = get(groups);
expect(state.groups).toEqual([]);
expect(state.loading).toBe(false);
expect(state.error).toBeNull();
});
it('loads groups', async () => {
await groups.load('board-1');
expect(get(groupsLoading)).toBe(false);
});
it('creates group', async () => {
const groupData = {
name: 'New Group',
color: '#FF5733',
image_ids: ['img1', 'img2'],
};
const group = await groups.create('board-1', groupData);
expect(group).not.toBeNull();
expect(get(groupCount)).toBe(1);
});
it('handles creation error', async () => {
const { createGroup } = await import('$lib/api/groups');
vi.mocked(createGroup).mockRejectedValueOnce(new Error('API Error'));
const group = await groups.create('board-1', {
name: 'Test',
color: '#FF5733',
image_ids: ['img1'],
});
expect(group).toBeNull();
expect(get(groupsError)).toBeTruthy();
});
it('deletes group', async () => {
// First create a group
await groups.create('board-1', {
name: 'Test',
color: '#FF5733',
image_ids: ['img1'],
});
expect(get(groupCount)).toBe(1);
// Then delete it
await groups.delete('board-1', 'group-1');
expect(get(groupCount)).toBe(0);
});
it('clears all groups', () => {
groups.clear();
const state = get(groups);
expect(state.groups).toEqual([]);
expect(state.loading).toBe(false);
expect(state.error).toBeNull();
});
});
describe('Ungroup Operations', () => {
beforeEach(() => {
vi.clearAllMocks();
});
it('ungroups images', async () => {
const result = await ungroupImages('board-1', 'group-1');
expect(result).toBe(true);
});
it('handles ungroup error', async () => {
const { deleteGroup } = await import('$lib/api/groups');
vi.mocked(deleteGroup).mockRejectedValueOnce(new Error('API Error'));
const result = await ungroupImages('board-1', 'group-1');
expect(result).toBe(false);
});
it('removes specific images from group', async () => {
vi.mock('$lib/api/client', () => ({
apiClient: {
patch: vi.fn().mockResolvedValue({}),
},
}));
const result = await removeImagesFromGroup('board-1', 'group-1', ['img1', 'img2']);
expect(result).toBe(true);
});
});