transcendent.test.ts
1 /** 2 * Tests for transcendent workspace module 3 */ 4 import { describe, it, expect } from 'vitest'; 5 import { 6 getTranscendentWorkspaces, 7 getNonTranscendentWorkspaces, 8 countTranscendent, 9 canBeTranscendent, 10 validateTranscendentChange, 11 suggestsTranscendent, 12 formatWindowTitle, 13 getTranscendentStats, 14 sortTranscendentFirst, 15 groupByTranscendent, 16 } from '../../../src/lib/workspace/transcendent'; 17 import type { Workspace, ParentWorkspace, ChildWorkspace, StandaloneWorkspace } from '../../../src/lib/types'; 18 19 // Helper to create mock workspaces 20 function createMockWorkspace( 21 id: string, 22 type: 'parent' | 'child' | 'standalone', 23 isTranscendent: boolean, 24 name = `Workspace ${id}` 25 ): Workspace { 26 const base = { 27 id, 28 name, 29 isTranscendent, 30 metadata: { 31 created: new Date().toISOString(), 32 lastModified: new Date().toISOString(), 33 lastAccessed: new Date().toISOString(), 34 switchCount: 0, 35 totalTimeActive: 0, 36 }, 37 }; 38 39 if (type === 'parent') { 40 return { ...base, type: 'parent', parentId: null, children: [] } as ParentWorkspace; 41 } else if (type === 'child') { 42 return { ...base, type: 'child', parentId: 'parent-1', tabs: [] } as ChildWorkspace; 43 } else { 44 return { ...base, type: 'standalone', parentId: null, tabs: [] } as StandaloneWorkspace; 45 } 46 } 47 48 describe('getTranscendentWorkspaces', () => { 49 it('returns only transcendent workspaces', () => { 50 const workspaces = [ 51 createMockWorkspace('1', 'standalone', true), 52 createMockWorkspace('2', 'child', false), 53 createMockWorkspace('3', 'standalone', true), 54 ]; 55 56 const result = getTranscendentWorkspaces(workspaces); 57 58 expect(result).toHaveLength(2); 59 expect(result.every((ws) => ws.isTranscendent)).toBe(true); 60 }); 61 62 it('returns empty array when no transcendent workspaces', () => { 63 const workspaces = [ 64 createMockWorkspace('1', 'standalone', false), 65 createMockWorkspace('2', 'parent', false), 66 ]; 67 68 const result = getTranscendentWorkspaces(workspaces); 69 70 expect(result).toHaveLength(0); 71 }); 72 }); 73 74 describe('getNonTranscendentWorkspaces', () => { 75 it('returns only non-transcendent workspaces', () => { 76 const workspaces = [ 77 createMockWorkspace('1', 'standalone', true), 78 createMockWorkspace('2', 'child', false), 79 createMockWorkspace('3', 'parent', false), 80 ]; 81 82 const result = getNonTranscendentWorkspaces(workspaces); 83 84 expect(result).toHaveLength(2); 85 expect(result.every((ws) => !ws.isTranscendent)).toBe(true); 86 }); 87 }); 88 89 describe('countTranscendent', () => { 90 it('counts transcendent workspaces', () => { 91 const workspaces = [ 92 createMockWorkspace('1', 'standalone', true), 93 createMockWorkspace('2', 'child', false), 94 createMockWorkspace('3', 'standalone', true), 95 createMockWorkspace('4', 'parent', false), 96 ]; 97 98 expect(countTranscendent(workspaces)).toBe(2); 99 }); 100 101 it('returns 0 for empty array', () => { 102 expect(countTranscendent([])).toBe(0); 103 }); 104 }); 105 106 describe('canBeTranscendent', () => { 107 it('returns false for parent workspaces', () => { 108 const parent = createMockWorkspace('1', 'parent', false); 109 expect(canBeTranscendent(parent)).toBe(false); 110 }); 111 112 it('returns true for child workspaces', () => { 113 const child = createMockWorkspace('1', 'child', false); 114 expect(canBeTranscendent(child)).toBe(true); 115 }); 116 117 it('returns true for standalone workspaces', () => { 118 const standalone = createMockWorkspace('1', 'standalone', false); 119 expect(canBeTranscendent(standalone)).toBe(true); 120 }); 121 }); 122 123 describe('validateTranscendentChange', () => { 124 it('rejects making parent workspace transcendent', () => { 125 const parent = createMockWorkspace('1', 'parent', false); 126 const result = validateTranscendentChange(parent, true); 127 128 expect(result.valid).toBe(false); 129 expect(result.reason).toBeTruthy(); 130 }); 131 132 it('allows making child workspace transcendent', () => { 133 const child = createMockWorkspace('1', 'child', false); 134 const result = validateTranscendentChange(child, true); 135 136 expect(result.valid).toBe(true); 137 }); 138 139 it('allows making standalone workspace transcendent', () => { 140 const standalone = createMockWorkspace('1', 'standalone', false); 141 const result = validateTranscendentChange(standalone, true); 142 143 expect(result.valid).toBe(true); 144 }); 145 146 it('allows removing transcendent status from any workspace', () => { 147 const parent = createMockWorkspace('1', 'parent', true); 148 const result = validateTranscendentChange(parent, false); 149 150 expect(result.valid).toBe(true); 151 }); 152 }); 153 154 describe('suggestsTranscendent', () => { 155 it('returns true for email-related names', () => { 156 expect(suggestsTranscendent('Email')).toBe(true); 157 expect(suggestsTranscendent('Gmail')).toBe(true); // Contains 'mail' 158 expect(suggestsTranscendent('My Email Workspace')).toBe(true); 159 }); 160 161 it('returns true for chat-related names', () => { 162 expect(suggestsTranscendent('Chat')).toBe(true); 163 expect(suggestsTranscendent('Slack')).toBe(true); 164 expect(suggestsTranscendent('Discord Server')).toBe(true); 165 }); 166 167 it('returns true for music-related names', () => { 168 expect(suggestsTranscendent('Music')).toBe(true); 169 expect(suggestsTranscendent('Spotify')).toBe(true); 170 }); 171 172 it('returns false for unrelated names', () => { 173 expect(suggestsTranscendent('Project Alpha')).toBe(false); 174 expect(suggestsTranscendent('Development')).toBe(false); 175 expect(suggestsTranscendent('Research')).toBe(false); 176 }); 177 }); 178 179 describe('formatWindowTitle', () => { 180 it('includes infinity symbol for transcendent workspace', () => { 181 const ws = createMockWorkspace('1', 'standalone', true, 'My Workspace'); 182 const title = formatWindowTitle(ws); 183 184 expect(title).toContain('\u221E'); // Infinity symbol 185 expect(title).toContain('My Workspace'); 186 expect(title).toContain('Mnemonic'); 187 }); 188 189 it('excludes infinity symbol for non-transcendent workspace', () => { 190 const ws = createMockWorkspace('1', 'standalone', false, 'My Workspace'); 191 const title = formatWindowTitle(ws); 192 193 expect(title).not.toContain('\u221E'); 194 }); 195 196 it('includes type indicator', () => { 197 const parent = createMockWorkspace('1', 'parent', false); 198 const child = createMockWorkspace('2', 'child', false); 199 const standalone = createMockWorkspace('3', 'standalone', false); 200 201 expect(formatWindowTitle(parent)).toContain('[+]'); 202 expect(formatWindowTitle(child)).toContain('[-]'); 203 expect(formatWindowTitle(standalone)).toContain('[*]'); 204 }); 205 }); 206 207 describe('getTranscendentStats', () => { 208 it('returns correct statistics', () => { 209 const workspaces: Workspace[] = [ 210 { ...createMockWorkspace('1', 'child', true), tabs: [{ url: 'a', title: 'a', pinned: false }] } as ChildWorkspace, 211 { ...createMockWorkspace('2', 'standalone', true), tabs: [{ url: 'b', title: 'b', pinned: false }, { url: 'c', title: 'c', pinned: false }] } as StandaloneWorkspace, 212 createMockWorkspace('3', 'parent', false), 213 ]; 214 215 const stats = getTranscendentStats(workspaces); 216 217 expect(stats.total).toBe(2); 218 expect(stats.byType.child).toBe(1); 219 expect(stats.byType.standalone).toBe(1); 220 expect(stats.totalTabs).toBe(3); 221 }); 222 }); 223 224 describe('sortTranscendentFirst', () => { 225 it('sorts transcendent workspaces before non-transcendent', () => { 226 const workspaces = [ 227 createMockWorkspace('1', 'standalone', false, 'Zebra'), 228 createMockWorkspace('2', 'child', true, 'Alpha'), 229 createMockWorkspace('3', 'standalone', false, 'Beta'), 230 ]; 231 232 const sorted = sortTranscendentFirst(workspaces); 233 234 expect(sorted[0].id).toBe('2'); // Transcendent first 235 expect(sorted[0].isTranscendent).toBe(true); 236 }); 237 238 it('maintains alphabetical order within groups', () => { 239 const workspaces = [ 240 createMockWorkspace('1', 'standalone', true, 'Zebra'), 241 createMockWorkspace('2', 'child', true, 'Alpha'), 242 createMockWorkspace('3', 'standalone', false, 'Beta'), 243 ]; 244 245 const sorted = sortTranscendentFirst(workspaces); 246 247 expect(sorted[0].name).toBe('Alpha'); 248 expect(sorted[1].name).toBe('Zebra'); 249 expect(sorted[2].name).toBe('Beta'); 250 }); 251 }); 252 253 describe('groupByTranscendent', () => { 254 it('groups workspaces by transcendent status', () => { 255 const workspaces = [ 256 createMockWorkspace('1', 'standalone', true), 257 createMockWorkspace('2', 'child', false), 258 createMockWorkspace('3', 'standalone', true), 259 createMockWorkspace('4', 'parent', false), 260 ]; 261 262 const { transcendent, regular } = groupByTranscendent(workspaces); 263 264 expect(transcendent).toHaveLength(2); 265 expect(regular).toHaveLength(2); 266 expect(transcendent.every((ws) => ws.isTranscendent)).toBe(true); 267 expect(regular.every((ws) => !ws.isTranscendent)).toBe(true); 268 }); 269 });