fix: replace hardcoded localhost:3001 with relative API URL + add canvas mock and integration test
- Fix frontend Network error: API_BASE now defaults to empty string (same-origin) - Fix 3 files: api.ts, AuthContext.tsx, Dashboard.tsx - Add Canvas 2D context mock in tests/setup.ts for jsdom - Fix -0 vs +0 in ZoomPanController.getViewport() - Add IntegrationWorkflow.test.ts (35 tests, full CAD workflow)
This commit is contained in:
@@ -0,0 +1,190 @@
|
||||
/**
|
||||
* GroupTool Tests – GroupManager: create, ungroup, nest, query
|
||||
*/
|
||||
import { describe, it, expect, beforeEach } from 'vitest';
|
||||
import { GroupManager } from '../src/tools/modification/GroupTool';
|
||||
import type { ElementGroup } from '../src/tools/modification/GroupTool';
|
||||
|
||||
describe('GroupManager', () => {
|
||||
let gm: GroupManager;
|
||||
|
||||
beforeEach(() => {
|
||||
gm = new GroupManager();
|
||||
});
|
||||
|
||||
describe('createGroup', () => {
|
||||
it('should create a group with element IDs', () => {
|
||||
const group = gm.createGroup(['el-1', 'el-2', 'el-3']);
|
||||
expect(group.id).toBeDefined();
|
||||
expect(group.elementIds).toEqual(['el-1', 'el-2', 'el-3']);
|
||||
expect(group.parentGroupId).toBeNull();
|
||||
});
|
||||
|
||||
it('should create a group with a custom name', () => {
|
||||
const group = gm.createGroup(['el-1'], 'My Group');
|
||||
expect(group.name).toBe('My Group');
|
||||
});
|
||||
|
||||
it('should create a group with default name when no name provided', () => {
|
||||
const group = gm.createGroup(['el-1']);
|
||||
expect(group.name).toContain('Group');
|
||||
});
|
||||
|
||||
it('should create multiple groups with unique IDs', () => {
|
||||
const g1 = gm.createGroup(['el-1']);
|
||||
const g2 = gm.createGroup(['el-2']);
|
||||
expect(g1.id).not.toBe(g2.id);
|
||||
});
|
||||
});
|
||||
|
||||
describe('ungroup', () => {
|
||||
it('should remove a group and return its element IDs', () => {
|
||||
const group = gm.createGroup(['el-1', 'el-2']);
|
||||
const ids = gm.ungroup(group.id);
|
||||
expect(ids).toEqual(['el-1', 'el-2']);
|
||||
expect(gm.getGroup(group.id)).toBeUndefined();
|
||||
});
|
||||
|
||||
it('should return empty array for non-existent group', () => {
|
||||
const ids = gm.ungroup('non-existent');
|
||||
expect(ids).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGroup', () => {
|
||||
it('should retrieve a group by ID', () => {
|
||||
const group = gm.createGroup(['el-1']);
|
||||
const retrieved = gm.getGroup(group.id);
|
||||
expect(retrieved).toBeDefined();
|
||||
expect(retrieved!.id).toBe(group.id);
|
||||
});
|
||||
|
||||
it('should return undefined for non-existent group', () => {
|
||||
expect(gm.getGroup('non-existent')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGroups', () => {
|
||||
it('should return all groups', () => {
|
||||
gm.createGroup(['el-1']);
|
||||
gm.createGroup(['el-2']);
|
||||
expect(gm.getGroups().length).toBe(2);
|
||||
});
|
||||
|
||||
it('should return empty array when no groups', () => {
|
||||
expect(gm.getGroups()).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGroupedElements', () => {
|
||||
it('should return set of all element IDs in all groups', () => {
|
||||
gm.createGroup(['el-1', 'el-2']);
|
||||
gm.createGroup(['el-3']);
|
||||
const ids = gm.getGroupedElements();
|
||||
expect(ids.size).toBe(3);
|
||||
expect(ids.has('el-1')).toBe(true);
|
||||
expect(ids.has('el-2')).toBe(true);
|
||||
expect(ids.has('el-3')).toBe(true);
|
||||
});
|
||||
|
||||
it('should return empty set when no groups', () => {
|
||||
expect(gm.getGroupedElements().size).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('getGroupForElement', () => {
|
||||
it('should return group ID for an element in a group', () => {
|
||||
const group = gm.createGroup(['el-1', 'el-2']);
|
||||
expect(gm.getGroupForElement('el-1')).toBe(group.id);
|
||||
expect(gm.getGroupForElement('el-2')).toBe(group.id);
|
||||
});
|
||||
|
||||
it('should return null for element not in any group', () => {
|
||||
expect(gm.getGroupForElement('el-lonely')).toBeNull();
|
||||
});
|
||||
});
|
||||
|
||||
describe('moveGroup', () => {
|
||||
it('should return element IDs that need to be moved', () => {
|
||||
const group = gm.createGroup(['el-1', 'el-2']);
|
||||
const ids = gm.moveGroup(group.id, 10, 20);
|
||||
expect(ids).toEqual(['el-1', 'el-2']);
|
||||
});
|
||||
|
||||
it('should return empty array for non-existent group', () => {
|
||||
const ids = gm.moveGroup('non-existent', 10, 20);
|
||||
expect(ids).toEqual([]);
|
||||
});
|
||||
});
|
||||
|
||||
describe('setParent (nested groups)', () => {
|
||||
it('should set parent group for nesting', () => {
|
||||
const parent = gm.createGroup(['el-1']);
|
||||
const child = gm.createGroup(['el-2']);
|
||||
gm.setParent(child.id, parent.id);
|
||||
expect(gm.getGroup(child.id)!.parentGroupId).toBe(parent.id);
|
||||
});
|
||||
|
||||
it('should prevent circular references', () => {
|
||||
const g1 = gm.createGroup(['el-1']);
|
||||
const g2 = gm.createGroup(['el-2']);
|
||||
gm.setParent(g1.id, g2.id);
|
||||
// Trying to set g2's parent to g1 would create a cycle: g1 -> g2 -> g1
|
||||
gm.setParent(g2.id, g1.id);
|
||||
expect(gm.getGroup(g2.id)!.parentGroupId).toBeNull();
|
||||
});
|
||||
|
||||
it('should allow setting parent to null', () => {
|
||||
const parent = gm.createGroup(['el-1']);
|
||||
const child = gm.createGroup(['el-2']);
|
||||
gm.setParent(child.id, parent.id);
|
||||
gm.setParent(child.id, null);
|
||||
expect(gm.getGroup(child.id)!.parentGroupId).toBeNull();
|
||||
});
|
||||
|
||||
it('should do nothing for non-existent group', () => {
|
||||
gm.setParent('non-existent', null);
|
||||
expect(gm.getGroup('non-existent')).toBeUndefined();
|
||||
});
|
||||
});
|
||||
|
||||
describe('clear', () => {
|
||||
it('should clear all groups', () => {
|
||||
gm.createGroup(['el-1']);
|
||||
gm.createGroup(['el-2']);
|
||||
gm.clear();
|
||||
expect(gm.getGroups().length).toBe(0);
|
||||
});
|
||||
});
|
||||
|
||||
describe('toJSON & fromJSON', () => {
|
||||
it('should serialize groups to JSON', () => {
|
||||
gm.createGroup(['el-1', 'el-2'], 'Group A');
|
||||
gm.createGroup(['el-3'], 'Group B');
|
||||
const json = gm.toJSON();
|
||||
expect(json.length).toBe(2);
|
||||
expect(json[0].name).toBe('Group A');
|
||||
expect(json[1].name).toBe('Group B');
|
||||
});
|
||||
|
||||
it('should restore groups from JSON', () => {
|
||||
const groups: ElementGroup[] = [
|
||||
{ id: 'grp-1', name: 'Restored A', elementIds: ['el-1'], parentGroupId: null },
|
||||
{ id: 'grp-2', name: 'Restored B', elementIds: ['el-2', 'el-3'], parentGroupId: 'grp-1' },
|
||||
];
|
||||
gm.fromJSON(groups);
|
||||
expect(gm.getGroups().length).toBe(2);
|
||||
expect(gm.getGroup('grp-1')!.name).toBe('Restored A');
|
||||
expect(gm.getGroup('grp-2')!.parentGroupId).toBe('grp-1');
|
||||
});
|
||||
|
||||
it('should clear existing groups when restoring from JSON', () => {
|
||||
gm.createGroup(['el-old']);
|
||||
gm.fromJSON([
|
||||
{ id: 'grp-new', name: 'New', elementIds: ['el-new'], parentGroupId: null },
|
||||
]);
|
||||
expect(gm.getGroups().length).toBe(1);
|
||||
expect(gm.getGroup('grp-new')).toBeDefined();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user