phase 10
All checks were successful
CI/CD Pipeline / VM Test - backend-integration (push) Successful in 7s
CI/CD Pipeline / VM Test - full-stack (push) Successful in 7s
CI/CD Pipeline / VM Test - performance (push) Successful in 7s
CI/CD Pipeline / VM Test - security (push) Successful in 7s
CI/CD Pipeline / Backend Linting (push) Successful in 2s
CI/CD Pipeline / Frontend Linting (push) Successful in 15s
CI/CD Pipeline / Nix Flake Check (push) Successful in 41s
CI/CD Pipeline / CI Summary (push) Successful in 1s

This commit is contained in:
Danilo Reyes
2025-11-02 14:26:15 -06:00
parent ce0b692aee
commit 3eb3d977f9
18 changed files with 3079 additions and 32 deletions

View File

@@ -0,0 +1,443 @@
/**
* Tests for clipboard operations (copy, cut, paste)
* Tests clipboard store and operations
*/
import { describe, it, expect, beforeEach } from 'vitest';
import { get } from 'svelte/store';
import {
clipboard,
hasClipboardContent,
clipboardCount,
isCutOperation,
} from '$lib/stores/clipboard';
import type { ClipboardImageData } from '$lib/stores/clipboard';
import {
copySelectedImages,
copyImages,
copySingleImage,
hasClipboardContent as hasContent,
getClipboardCount,
} from '$lib/canvas/clipboard/copy';
import { cutSelectedImages, cutImages, cutSingleImage } from '$lib/canvas/clipboard/cut';
import {
pasteFromClipboard,
pasteAtPosition,
canPaste,
getPastePreview,
} from '$lib/canvas/clipboard/paste';
import { selection } from '$lib/stores/selection';
import { viewport } from '$lib/stores/viewport';
describe('Clipboard Store', () => {
beforeEach(() => {
clipboard.clear();
selection.clearSelection();
});
it('starts empty', () => {
const state = get(clipboard);
expect(state.images).toEqual([]);
expect(state.operation).toBeNull();
});
it('stores copied images', () => {
const images: ClipboardImageData[] = [
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
];
clipboard.copy(images);
const state = get(clipboard);
expect(state.images).toHaveLength(1);
expect(state.operation).toBe('copy');
});
it('stores cut images', () => {
const images: ClipboardImageData[] = [
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
];
clipboard.cut(images);
const state = get(clipboard);
expect(state.images).toHaveLength(1);
expect(state.operation).toBe('cut');
});
it('clears clipboard', () => {
const images: ClipboardImageData[] = [
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
];
clipboard.copy(images);
clipboard.clear();
const state = get(clipboard);
expect(state.images).toEqual([]);
expect(state.operation).toBeNull();
});
it('hasClipboardContent reflects state', () => {
expect(get(hasClipboardContent)).toBe(false);
clipboard.copy([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
]);
expect(get(hasClipboardContent)).toBe(true);
});
it('clipboardCount reflects count', () => {
expect(get(clipboardCount)).toBe(0);
clipboard.copy([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
{
boardImageId: 'bi2',
imageId: 'img2',
position: { x: 200, y: 200 },
transformations: {},
zOrder: 1,
},
]);
expect(get(clipboardCount)).toBe(2);
});
it('isCutOperation reflects operation type', () => {
clipboard.copy([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
]);
expect(get(isCutOperation)).toBe(false);
clipboard.cut([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
]);
expect(get(isCutOperation)).toBe(true);
});
});
describe('Copy Operations', () => {
beforeEach(() => {
clipboard.clear();
selection.clearSelection();
});
it('copies selected images', () => {
selection.selectMultiple(['img1', 'img2']);
const getImageData = (id: string): ClipboardImageData | null => ({
boardImageId: `bi-${id}`,
imageId: id,
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
});
const count = copySelectedImages(getImageData);
expect(count).toBe(2);
expect(get(clipboardCount)).toBe(2);
expect(get(isCutOperation)).toBe(false);
});
it('copies specific images', () => {
const getImageData = (id: string): ClipboardImageData | null => ({
boardImageId: `bi-${id}`,
imageId: id,
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
});
const count = copyImages(['img1', 'img2', 'img3'], getImageData);
expect(count).toBe(3);
expect(get(clipboardCount)).toBe(3);
});
it('copies single image', () => {
const getImageData = (id: string): ClipboardImageData | null => ({
boardImageId: `bi-${id}`,
imageId: id,
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
});
const success = copySingleImage(getImageData, 'img1');
expect(success).toBe(true);
expect(get(clipboardCount)).toBe(1);
});
it('returns 0 when copying empty selection', () => {
const getImageData = (): ClipboardImageData | null => null;
const count = copySelectedImages(getImageData);
expect(count).toBe(0);
});
it('hasClipboardContent returns true after copy', () => {
const getImageData = (id: string): ClipboardImageData | null => ({
boardImageId: `bi-${id}`,
imageId: id,
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
});
copyImages(['img1'], getImageData);
expect(hasContent()).toBe(true);
expect(getClipboardCount()).toBe(1);
});
});
describe('Cut Operations', () => {
beforeEach(() => {
clipboard.clear();
selection.clearSelection();
});
it('cuts selected images', () => {
selection.selectMultiple(['img1', 'img2']);
const getImageData = (id: string): ClipboardImageData | null => ({
boardImageId: `bi-${id}`,
imageId: id,
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
});
const count = cutSelectedImages(getImageData);
expect(count).toBe(2);
expect(get(clipboardCount)).toBe(2);
expect(get(isCutOperation)).toBe(true);
});
it('cuts specific images', () => {
const getImageData = (id: string): ClipboardImageData | null => ({
boardImageId: `bi-${id}`,
imageId: id,
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
});
const count = cutImages(['img1', 'img2'], getImageData);
expect(count).toBe(2);
expect(get(isCutOperation)).toBe(true);
});
it('cuts single image', () => {
const getImageData = (id: string): ClipboardImageData | null => ({
boardImageId: `bi-${id}`,
imageId: id,
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
});
const success = cutSingleImage(getImageData, 'img1');
expect(success).toBe(true);
expect(get(clipboardCount)).toBe(1);
expect(get(isCutOperation)).toBe(true);
});
});
describe('Paste Operations', () => {
beforeEach(() => {
clipboard.clear();
viewport.reset();
});
it('pastes images at viewport center', () => {
clipboard.copy([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
{
boardImageId: 'bi2',
imageId: 'img2',
position: { x: 200, y: 100 },
transformations: {},
zOrder: 1,
},
]);
const pasted = pasteFromClipboard(800, 600);
expect(pasted).toHaveLength(2);
expect(pasted[0].newPosition).toBeDefined();
expect(pasted[1].newPosition).toBeDefined();
});
it('pastes at specific position', () => {
clipboard.copy([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
]);
const pasted = pasteAtPosition(500, 500);
expect(pasted).toHaveLength(1);
expect(pasted[0].newPosition.x).toBe(500);
expect(pasted[0].newPosition.y).toBe(500);
});
it('preserves relative positions when pasting', () => {
clipboard.copy([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
{
boardImageId: 'bi2',
imageId: 'img2',
position: { x: 200, y: 150 },
transformations: {},
zOrder: 1,
},
]);
const pasted = pasteAtPosition(0, 0);
// Relative distance should be preserved
const deltaX1 = pasted[0].newPosition.x;
const deltaX2 = pasted[1].newPosition.x;
expect(deltaX2 - deltaX1).toBe(100); // Original was 200 - 100 = 100
});
it('clears clipboard after cut paste', () => {
clipboard.cut([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
]);
pasteAtPosition(200, 200);
expect(get(hasClipboardContent)).toBe(false);
});
it('preserves clipboard after copy paste', () => {
clipboard.copy([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
]);
pasteAtPosition(200, 200);
expect(get(hasClipboardContent)).toBe(true);
});
it('returns empty array when pasting empty clipboard', () => {
const pasted = pasteFromClipboard(800, 600);
expect(pasted).toEqual([]);
});
it('canPaste reflects clipboard state', () => {
expect(canPaste()).toBe(false);
clipboard.copy([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
]);
expect(canPaste()).toBe(true);
});
it('getPastePreview shows where images will be pasted', () => {
clipboard.copy([
{
boardImageId: 'bi1',
imageId: 'img1',
position: { x: 100, y: 100 },
transformations: {},
zOrder: 0,
},
]);
const preview = getPastePreview(800, 600);
expect(preview).toHaveLength(1);
expect(preview[0]).toHaveProperty('x');
expect(preview[0]).toHaveProperty('y');
});
});