Skip to content

Commit fb2d2a3

Browse files
committed
e2e
1 parent ad72ed5 commit fb2d2a3

8 files changed

Lines changed: 613 additions & 0 deletions
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
/**
2+
* Canvas Selection — verifies Figma-like selection interactions:
3+
* click to select, shift-click for multi-select, click canvas to deselect,
4+
* and Escape to clear selection.
5+
*/
6+
import { test, expect } from './fixtures';
7+
8+
/** Helper: add N elements to the canvas by clicking palette items. */
9+
async function addElements(window: Awaited<ReturnType<typeof import('./fixtures').test['step']>>, count: number) {
10+
const items = window.locator('.palette-item');
11+
for (let i = 0; i < count; i++) {
12+
await items.nth(i % (await items.count())).click();
13+
}
14+
}
15+
16+
test.describe('Canvas Selection', () => {
17+
test('clicking a canvas element selects it', async ({ window }) => {
18+
await addElements(window, 1);
19+
20+
// Click the first added canvas element (skip the root wrapper)
21+
const elements = window.locator('.canvas-element');
22+
const addedElement = elements.last();
23+
await addedElement.click();
24+
25+
// Should be marked as selected
26+
await expect(addedElement).toHaveAttribute('data-selected', 'true');
27+
});
28+
29+
test('clicking empty canvas area clears selection', async ({ window }) => {
30+
await addElements(window, 1);
31+
32+
// Select the element first
33+
const element = window.locator('.canvas-element').last();
34+
await element.click();
35+
await expect(element).toHaveAttribute('data-selected', 'true');
36+
37+
// Click the canvas background (the .editor-canvas container)
38+
const canvas = window.locator('.editor-canvas');
39+
// Click at the very bottom-right of the canvas to hit the background
40+
const box = await canvas.boundingBox();
41+
if (box) {
42+
await window.mouse.click(box.x + box.width - 10, box.y + box.height - 10);
43+
}
44+
45+
// Selection should be cleared
46+
const selectedElements = window.locator('.canvas-element[data-selected="true"]');
47+
expect(await selectedElements.count()).toBe(0);
48+
});
49+
50+
test('pressing Escape clears selection', async ({ window }) => {
51+
await addElements(window, 1);
52+
53+
const element = window.locator('.canvas-element').last();
54+
await element.click();
55+
await expect(element).toHaveAttribute('data-selected', 'true');
56+
57+
// Press Escape
58+
await window.keyboard.press('Escape');
59+
60+
// Selection should be cleared
61+
const selected = window.locator('.canvas-element[data-selected="true"]');
62+
expect(await selected.count()).toBe(0);
63+
});
64+
65+
test('selecting an element shows its properties', async ({ window }) => {
66+
await addElements(window, 1);
67+
68+
// Switch right panel to Properties
69+
const propsTab = window.locator('.editor-right-panel button.panel-tab', { hasText: 'Properties' });
70+
await propsTab.click();
71+
72+
// Before selection: empty state
73+
await expect(window.locator('.empty-state')).toBeVisible();
74+
75+
// Select the element
76+
await window.locator('.canvas-element').last().click();
77+
78+
// Properties panel should now show element info
79+
const propsPanel = window.locator('.props-panel');
80+
await expect(propsPanel).toBeVisible();
81+
await expect(propsPanel.locator('.props-section-title', { hasText: 'Element' })).toBeVisible();
82+
await expect(propsPanel.locator('.props-section-title', { hasText: 'Properties' })).toBeVisible();
83+
});
84+
});
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
/**
2+
* Component Palette — verifies palette grid, category headings,
3+
* and adding components to the canvas via click.
4+
*/
5+
import { test, expect } from './fixtures';
6+
7+
test.describe('Component Palette', () => {
8+
test('palette grid shows categorized components', async ({ window }) => {
9+
// Ensure the Components tab is active (default)
10+
const componentsTab = window.locator('button.panel-tab', { hasText: 'Components' });
11+
await expect(componentsTab).toHaveAttribute('data-active', 'true');
12+
13+
// Palette grid is visible with at least one category heading
14+
const paletteGrid = window.locator('.palette-grid');
15+
await expect(paletteGrid).toBeVisible();
16+
17+
const categories = paletteGrid.locator('.palette-category');
18+
expect(await categories.count()).toBeGreaterThan(0);
19+
20+
// Each category has at least one palette item beneath it
21+
const items = paletteGrid.locator('.palette-item');
22+
expect(await items.count()).toBeGreaterThan(0);
23+
});
24+
25+
test('palette items are draggable', async ({ window }) => {
26+
const firstItem = window.locator('.palette-item').first();
27+
await expect(firstItem).toBeVisible();
28+
await expect(firstItem).toHaveAttribute('draggable', 'true');
29+
});
30+
31+
test('clicking a palette item adds an element to the canvas', async ({ window }) => {
32+
// Count existing canvas elements before
33+
const canvasBefore = await window.locator('.canvas-element').count();
34+
35+
// Click the first palette item
36+
const firstItem = window.locator('.palette-item').first();
37+
await firstItem.click();
38+
39+
// A new canvas element should appear
40+
const canvasAfter = await window.locator('.canvas-element').count();
41+
expect(canvasAfter).toBeGreaterThan(canvasBefore);
42+
});
43+
44+
test('clicking a palette item adds a layer to the layer tree', async ({ window }) => {
45+
// Add an element first
46+
await window.locator('.palette-item').first().click();
47+
48+
// Switch to Layers tab
49+
const layersTab = window.locator('.editor-left-panel button.panel-tab', { hasText: 'Layers' });
50+
await layersTab.click();
51+
await expect(layersTab).toHaveAttribute('data-active', 'true');
52+
53+
// Layer tree should have at least 2 items (root + the added element)
54+
const layerItems = window.locator('.layer-item');
55+
expect(await layerItems.count()).toBeGreaterThanOrEqual(2);
56+
});
57+
});
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
/**
2+
* Editor Layout — verifies the Figma-like shell renders correctly.
3+
*
4+
* Covers: titlebar, toolbar, left panel (Components/Layers tabs),
5+
* canvas, right panel (Properties/Code/AI Chat tabs), and resize handles.
6+
*/
7+
import { test, expect } from './fixtures';
8+
9+
test.describe('Editor Layout', () => {
10+
test('titlebar shows app name and file indicator', async ({ window }) => {
11+
const titlebar = window.locator('.titlebar');
12+
await expect(titlebar).toBeVisible();
13+
await expect(titlebar).toContainText('DesignForge');
14+
});
15+
16+
test('toolbar renders with all action groups', async ({ window }) => {
17+
const toolbar = window.locator('.editor-toolbar');
18+
await expect(toolbar).toBeVisible();
19+
20+
// File actions
21+
await expect(window.locator('.file-btn', { hasText: 'New' })).toBeVisible();
22+
await expect(window.locator('.file-btn', { hasText: 'Open' })).toBeVisible();
23+
await expect(window.locator('.file-btn', { hasText: 'Save' })).toBeVisible();
24+
await expect(window.locator('.file-btn', { hasText: 'Export Code' })).toBeVisible();
25+
26+
// History buttons (undo / redo)
27+
await expect(window.locator('.toolbar-btn[title*="Undo"]')).toBeVisible();
28+
await expect(window.locator('.toolbar-btn[title*="Redo"]')).toBeVisible();
29+
30+
// Preview toggle
31+
await expect(window.locator('.preview-mode-toggle')).toBeVisible();
32+
33+
// Settings
34+
await expect(window.locator('.toolbar-btn[title*="Settings"]')).toBeVisible();
35+
});
36+
37+
test('left panel shows Components and Layers tabs', async ({ window }) => {
38+
const leftPanel = window.locator('.editor-left-panel');
39+
await expect(leftPanel).toBeVisible();
40+
41+
const componentsTab = leftPanel.locator('button.panel-tab', { hasText: 'Components' });
42+
const layersTab = leftPanel.locator('button.panel-tab', { hasText: 'Layers' });
43+
44+
await expect(componentsTab).toBeVisible();
45+
await expect(layersTab).toBeVisible();
46+
47+
// Components tab is active by default
48+
await expect(componentsTab).toHaveAttribute('data-active', 'true');
49+
});
50+
51+
test('right panel shows Properties, Code, and AI Chat tabs', async ({ window }) => {
52+
const rightPanel = window.locator('.editor-right-panel');
53+
await expect(rightPanel).toBeVisible();
54+
55+
await expect(rightPanel.locator('button.panel-tab', { hasText: 'Properties' })).toBeVisible();
56+
await expect(rightPanel.locator('button.panel-tab', { hasText: 'Code' })).toBeVisible();
57+
await expect(rightPanel.locator('button.panel-tab', { hasText: 'AI Chat' })).toBeVisible();
58+
});
59+
60+
test('canvas area is visible between panels', async ({ window }) => {
61+
const canvas = window.locator('.editor-canvas');
62+
await expect(canvas).toBeVisible();
63+
64+
// Canvas frame exists inside
65+
const frame = window.locator('.canvas-frame');
66+
await expect(frame).toBeVisible();
67+
});
68+
69+
test('both resize handles are present', async ({ window }) => {
70+
await expect(window.locator('.resize-handle--left')).toBeVisible();
71+
await expect(window.locator('.resize-handle--right')).toBeVisible();
72+
});
73+
});
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/**
2+
* Keyboard Shortcuts — verifies the Figma-like keybindings work correctly.
3+
*/
4+
import { test, expect } from './fixtures';
5+
6+
test.describe('Keyboard Shortcuts', () => {
7+
test('Ctrl+D duplicates the selected element', async ({ window }) => {
8+
// Add an element
9+
await window.locator('.palette-item').first().click();
10+
const countAfterAdd = await window.locator('.canvas-element').count();
11+
12+
// Select the element
13+
await window.locator('.canvas-element').last().click();
14+
15+
// Ctrl+D to duplicate
16+
await window.keyboard.press('Control+d');
17+
18+
const countAfterDuplicate = await window.locator('.canvas-element').count();
19+
expect(countAfterDuplicate).toBeGreaterThan(countAfterAdd);
20+
});
21+
22+
test('Ctrl+A selects all elements', async ({ window }) => {
23+
// Add two elements
24+
await window.locator('.palette-item').first().click();
25+
await window.locator('.palette-item').nth(1).click();
26+
27+
// Ctrl+A to select all
28+
await window.keyboard.press('Control+a');
29+
30+
// Multiple elements should be selected (data-selected="true")
31+
const selectedCount = await window.locator('.canvas-element[data-selected="true"]').count();
32+
expect(selectedCount).toBeGreaterThanOrEqual(2);
33+
});
34+
35+
test('Escape clears selection', async ({ window }) => {
36+
await window.locator('.palette-item').first().click();
37+
await window.locator('.canvas-element').last().click();
38+
39+
// Verify something is selected
40+
expect(await window.locator('.canvas-element[data-selected="true"]').count()).toBeGreaterThanOrEqual(1);
41+
42+
// Escape to clear
43+
await window.keyboard.press('Escape');
44+
45+
expect(await window.locator('.canvas-element[data-selected="true"]').count()).toBe(0);
46+
});
47+
48+
test('Delete/Backspace removes selected element', async ({ window }) => {
49+
await window.locator('.palette-item').first().click();
50+
const countBefore = await window.locator('.canvas-element').count();
51+
52+
await window.locator('.canvas-element').last().click();
53+
await window.keyboard.press('Backspace');
54+
55+
const countAfter = await window.locator('.canvas-element').count();
56+
expect(countAfter).toBeLessThan(countBefore);
57+
});
58+
59+
test('Ctrl+, opens settings', async ({ window }) => {
60+
await window.keyboard.press('Control+,');
61+
62+
// Settings page/overlay should appear
63+
// (the SettingsPage component is rendered by the App)
64+
await window.waitForTimeout(500);
65+
66+
// Look for settings-related content
67+
const settingsVisible = await window.locator('[class*="settings"]').first().isVisible().catch(() => false);
68+
expect(settingsVisible).toBe(true);
69+
});
70+
});
Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
/**
2+
* Layer Tree — verifies the Figma-like layer panel:
3+
* switching tabs, layer items matching canvas elements,
4+
* clicking layers to select canvas elements, and hover highlighting.
5+
*/
6+
import { test, expect } from './fixtures';
7+
8+
test.describe('Layer Tree', () => {
9+
test('layers tab shows root element by default', async ({ window }) => {
10+
// Switch to Layers tab
11+
const layersTab = window.locator('.editor-left-panel button.panel-tab', { hasText: 'Layers' });
12+
await layersTab.click();
13+
14+
const layerTree = window.locator('.layer-tree');
15+
await expect(layerTree).toBeVisible();
16+
17+
// At least the root element is shown
18+
const layerItems = layerTree.locator('.layer-item');
19+
expect(await layerItems.count()).toBeGreaterThanOrEqual(1);
20+
});
21+
22+
test('adding an element creates a new layer item', async ({ window }) => {
23+
// Switch to Layers tab
24+
const layersTab = window.locator('.editor-left-panel button.panel-tab', { hasText: 'Layers' });
25+
await layersTab.click();
26+
27+
const layersBefore = await window.locator('.layer-item').count();
28+
29+
// Switch back to Components to add an element
30+
await window.locator('.editor-left-panel button.panel-tab', { hasText: 'Components' }).click();
31+
await window.locator('.palette-item').first().click();
32+
33+
// Switch back to Layers
34+
await layersTab.click();
35+
36+
const layersAfter = await window.locator('.layer-item').count();
37+
expect(layersAfter).toBeGreaterThan(layersBefore);
38+
});
39+
40+
test('clicking a layer item selects the corresponding canvas element', async ({ window }) => {
41+
// Add an element
42+
await window.locator('.palette-item').first().click();
43+
44+
// Switch to Layers tab
45+
const layersTab = window.locator('.editor-left-panel button.panel-tab', { hasText: 'Layers' });
46+
await layersTab.click();
47+
48+
// Click the last layer item (the newly added element)
49+
const layerItems = window.locator('.layer-item');
50+
const lastLayer = layerItems.last();
51+
await lastLayer.click();
52+
53+
// The layer should be marked selected
54+
await expect(lastLayer).toHaveAttribute('data-selected', 'true');
55+
56+
// A canvas element should also be selected
57+
const selectedCanvas = window.locator('.canvas-element[data-selected="true"]');
58+
expect(await selectedCanvas.count()).toBeGreaterThanOrEqual(1);
59+
});
60+
61+
test('layer items display element type names', async ({ window }) => {
62+
// Add an element
63+
await window.locator('.palette-item').first().click();
64+
65+
// Switch to Layers tab
66+
await window.locator('.editor-left-panel button.panel-tab', { hasText: 'Layers' }).click();
67+
68+
// Each layer item should have a visible name
69+
const layerNames = window.locator('.layer-item .layer-item-name');
70+
const count = await layerNames.count();
71+
expect(count).toBeGreaterThanOrEqual(1);
72+
73+
for (let i = 0; i < count; i++) {
74+
const name = await layerNames.nth(i).textContent();
75+
expect(name?.trim().length).toBeGreaterThan(0);
76+
}
77+
});
78+
});

0 commit comments

Comments
 (0)