diff --git a/tests/unit/utils.test.ts b/tests/unit/utils.test.ts new file mode 100644 index 0000000..a3fad4b --- /dev/null +++ b/tests/unit/utils.test.ts @@ -0,0 +1,210 @@ +import { OSUtils, osUtils } from '../../src/utils/osUtils'; +import { ShellUtils, shellUtils } from '../../src/utils/shellUtils'; +import { TemplateUtils, templateUtils } from '../../src/utils/templateUtils'; +import { Layout, Template, TerminalType } from '../../src/models/types'; + +describe('OSUtils', () => { + describe('getPlatform', () => { + it('should return current platform', () => { + const platform = osUtils.getPlatform(); + expect(['darwin', 'linux', 'win32']).toContain(platform); + }); + }); + + describe('isMacOS', () => { + it('should return true on macOS', () => { + const isMac = osUtils.isMacOS(); + expect(typeof isMac).toBe('boolean'); + }); + }); + + describe('isLinux', () => { + it('should return true on Linux', () => { + const isLinux = osUtils.isLinux(); + expect(typeof isLinux).toBe('boolean'); + }); + }); + + describe('getHomeDir', () => { + it('should return home directory', () => { + const homeDir = osUtils.getHomeDir(); + expect(homeDir).toBeTruthy(); + expect(homeDir.startsWith('/home') || homeDir.startsWith('/Users')).toBe(true); + }); + }); + + describe('expandPath', () => { + it('should expand tilde to home directory', () => { + const home = osUtils.getHomeDir(); + const expanded = osUtils.expandPath('~/test'); + expect(expanded).toBe(`${home}/test`); + }); + + it('should not modify absolute paths', () => { + const expanded = osUtils.expandPath('/usr/local/bin'); + expect(expanded).toBe('/usr/local/bin'); + }); + }); + + describe('detectTerminalType', () => { + it('should detect terminal type based on platform', () => { + const terminalType = osUtils.detectTerminalType(); + expect(['tmux', 'screen', 'iterm2', 'unknown']).toContain(terminalType); + }); + }); +}); + +describe('ShellUtils', () => { + describe('isCommandAvailable', () => { + it('should return boolean for valid command', async () => { + const available = await shellUtils.isCommandAvailable('ls'); + expect(typeof available).toBe('boolean'); + }); + + it('should return false for invalid command', async () => { + const available = await shellUtils.isCommandAvailable('__invalid_command_12345__'); + expect(available).toBe(false); + }); + }); + + describe('exec', () => { + it('should return result object for command', async () => { + const result = await shellUtils.exec('echo "test"'); + expect(typeof result.success).toBe('boolean'); + expect(typeof result.stdout).toBe('string'); + expect(typeof result.stderr).toBe('string'); + expect(typeof result.exitCode).toBe('number'); + }); + + it('should return failure for invalid command', async () => { + const result = await shellUtils.exec('__invalid_command_12345__'); + expect(result.success).toBe(false); + }); + }); +}); + +describe('TemplateUtils', () => { + describe('extractVariables', () => { + it('should extract variables from template', () => { + const template: Template = { + name: 'test', + displayName: 'Test', + description: 'Test template', + variables: [ + { name: 'PROJECT_DIR', description: 'Project dir', required: true }, + { name: 'SERVER_URL', description: 'Server URL', required: false }, + ], + layout: { + version: '1.0.0', + terminalType: 'tmux' as TerminalType, + createdAt: '2024-01-01T00:00:00.000Z', + updatedAt: '2024-01-01T00:00:00.000Z', + session: { + id: 's1', + name: 'test', + windows: [], + activeWindowIndex: 0, + }, + }, + }; + + const layoutWithVars: Layout = { + ...template.layout, + session: { + ...template.layout.session, + windows: [ + { + id: 'w1', + index: 0, + name: 'main', + panes: [ + { + id: 'p1', + index: 0, + layout: { x: 0, y: 0, width: 80, height: 24 }, + command: 'cd ${PROJECT_DIR} && npm run dev', + cwd: '${PROJECT_DIR}', + }, + ], + }, + ], + }, + }; + + const variables = templateUtils.extractVariables({ ...template, layout: layoutWithVars }); + expect(variables).toContain('PROJECT_DIR'); + }); + }); + + describe('substituteVariables', () => { + it('should substitute variables in layout', () => { + const layout: Layout = { + version: '1.0.0', + terminalType: 'tmux' as TerminalType, + createdAt: '2024-01-01T00:00:00.000Z', + updatedAt: '2024-01-01T00:00:00.000Z', + session: { + id: 's1', + name: 'test', + windows: [ + { + id: 'w1', + index: 0, + name: 'main', + panes: [ + { + id: 'p1', + index: 0, + layout: { x: 0, y: 0, width: 80, height: 24 }, + command: 'cd ${PROJECT_DIR} && npm run dev', + cwd: '${PROJECT_DIR}', + }, + ], + }, + ], + activeWindowIndex: 0, + }, + }; + + const variables: Record = { + PROJECT_DIR: '/home/user/projects/myapp', + }; + + const substituted = templateUtils.substituteVariables(layout, variables); + expect(substituted.session.windows[0].panes[0].command).toBe( + 'cd /home/user/projects/myapp && npm run dev' + ); + expect(substituted.session.windows[0].panes[0].cwd).toBe('/home/user/projects/myapp'); + }); + }); + + describe('validateTemplateVariables', () => { + it('should return missing required variables', () => { + const template: Template = { + name: 'test', + displayName: 'Test', + description: 'Test template', + variables: [ + { name: 'REQUIRED_VAR', description: 'Required', required: true }, + { name: 'OPTIONAL_VAR', description: 'Optional', required: false, defaultValue: 'default' }, + ], + layout: { + version: '1.0.0', + terminalType: 'tmux' as TerminalType, + createdAt: '2024-01-01T00:00:00.000Z', + updatedAt: '2024-01-01T00:00:00.000Z', + session: { + id: 's1', + name: 'test', + windows: [], + activeWindowIndex: 0, + }, + }, + }; + + const missing = templateUtils.validateTemplateVariables(template, {}); + expect(missing).toContain('REQUIRED_VAR'); + expect(missing).not.toContain('OPTIONAL_VAR'); + }); + }); +});