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
220 lines
5.4 KiB
TypeScript
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);
|
|
});
|
|
});
|