Skip to main content
Glama
adb.test.ts11.1 kB
import { execSync } from 'child_process'; import { checkADBInstalled, executeADBCommand, parseDeviceList, getConnectedDevices, getFirstAvailableDevice, captureScreenshot, getDeviceInfo, } from '../../src/utils/adb'; import { ADBNotFoundError, DeviceNotFoundError, NoDevicesFoundError } from '../../src/types'; // Mock execSync jest.mock('child_process', () => ({ execSync: jest.fn(), })); describe('ADB Utilities', () => { const mockExecSync = execSync as jest.MockedFunction<typeof execSync>; beforeEach(() => { jest.clearAllMocks(); }); describe('checkADBInstalled', () => { it('should return true if ADB is installed', () => { mockExecSync.mockReturnValue(Buffer.from('Android Debug Bridge version 1.0.41')); const result = checkADBInstalled(); expect(result).toBe(true); expect(mockExecSync).toHaveBeenCalledWith('adb version', { stdio: 'pipe', timeout: 5000 }); }); it('should return false if ADB is not installed', () => { mockExecSync.mockImplementation(() => { throw new Error('Command not found'); }); const result = checkADBInstalled(); expect(result).toBe(false); }); }); describe('executeADBCommand', () => { it('should execute ADB command successfully', () => { mockExecSync.mockReturnValue(Buffer.from('List of devices attached')); const result = executeADBCommand('devices'); expect(result).toBe('List of devices attached'); expect(mockExecSync).toHaveBeenCalledWith('adb devices', { stdio: 'pipe', timeout: 5000 }); }); it('should throw ADBNotFoundError if ADB is not installed', () => { // Mock execSync to simulate ADB not found mockExecSync.mockImplementation(() => { throw new Error('Command not found'); }); expect(() => executeADBCommand('devices')).toThrow(ADBNotFoundError); }); it('should throw ADBCommandError if command fails', () => { // First call for ADB version check succeeds mockExecSync.mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')); // Second call for the actual command fails mockExecSync.mockImplementationOnce(() => { const error = new Error('Command failed') as any; error.status = 1; error.stdout = ''; throw error; }); expect(() => executeADBCommand('invalid-command')).toThrow( 'ADB command failed: Command failed' ); }); it('should return stdout even if command returns error status 1', () => { // First call for ADB version check succeeds mockExecSync.mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')); // Second call returns error with stdout const error = new Error('Command failed') as any; error.status = 1; error.stdout = Buffer.from('Some output'); mockExecSync.mockImplementationOnce(() => { throw error; }); const result = executeADBCommand('devices'); expect(result).toBe('Some output'); }); }); describe('parseDeviceList', () => { it('should parse device list correctly', () => { const output = `List of devices attached emulator-5554 device product:sdk_gphone_x86 model:sdk_gphone_x86 device:generic_x86 transport_id:1 192.168.1.100:5555 unauthorized product:pixel model:pixel device:pixel transport_id:2`; const devices = parseDeviceList(output); expect(devices).toHaveLength(2); expect(devices[0]).toEqual({ id: 'emulator-5554', status: 'device', product: 'sdk_gphone_x86', model: 'sdk_gphone_x86', transportId: '1', }); expect(devices[1]).toEqual({ id: '192.168.1.100:5555', status: 'unauthorized', product: 'pixel', model: 'pixel', transportId: '2', }); }); it('should return empty array for empty device list', () => { const output = 'List of devices attached'; const devices = parseDeviceList(output); expect(devices).toHaveLength(0); }); it('should handle malformed lines gracefully', () => { const output = `List of devices attached emulator-5554 device malformed-line 192.168.1.100:5555 unauthorized`; const devices = parseDeviceList(output); expect(devices).toHaveLength(2); expect(devices[0].id).toBe('emulator-5554'); expect(devices[1].id).toBe('192.168.1.100:5555'); }); }); describe('getConnectedDevices', () => { it('should return list of connected devices', () => { const output = `List of devices attached emulator-5554 device product:sdk_gphone_x86 model:sdk_gphone_x86 transport_id:1`; mockExecSync .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check .mockReturnValueOnce(Buffer.from(output)); // devices -l call const devices = getConnectedDevices(); expect(devices).toHaveLength(1); expect(devices[0].id).toBe('emulator-5554'); expect(mockExecSync).toHaveBeenCalledWith('adb devices -l', { stdio: 'pipe', timeout: 5000 }); }); it('should throw NoDevicesFoundError if no devices are connected', () => { const output = 'List of devices attached'; mockExecSync .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check .mockReturnValueOnce(Buffer.from(output)); // devices -l call expect(() => getConnectedDevices()).toThrow(NoDevicesFoundError); }); it('should throw ADBCommandError if command fails', () => { mockExecSync.mockImplementation(() => { throw new Error('Command failed'); }); expect(() => getConnectedDevices()).toThrow('Failed to list connected devices'); }); }); describe('getFirstAvailableDevice', () => { it('should return the first available device', () => { const output = `List of devices attached emulator-5554 device product:sdk_gphone_x86 192.168.1.100:5555 unauthorized product:pixel`; mockExecSync .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check .mockReturnValueOnce(Buffer.from(output)); // devices -l call const device = getFirstAvailableDevice(); expect(device.id).toBe('emulator-5554'); expect(device.status).toBe('device'); }); it('should throw ADBCommandError if no available devices', () => { const output = `List of devices attached 192.168.1.100:5555 unauthorized product:pixel`; mockExecSync .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check .mockReturnValueOnce(Buffer.from(output)); // devices -l call expect(() => getFirstAvailableDevice()).toThrow('No available devices found'); }); }); describe('captureScreenshot', () => { it('should capture screenshot from specified device', () => { const deviceListOutput = `List of devices attached emulator-5554 device product:sdk_gphone_x86`; const screenshotOutput = 'fake-png-data'; // Simulate PNG data as string mockExecSync .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check for getConnectedDevices .mockReturnValueOnce(Buffer.from(deviceListOutput)) // devices -l call .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check for screenshot .mockReturnValueOnce(Buffer.from(screenshotOutput)); // screenshot capture const screenshot = captureScreenshot('emulator-5554'); expect(screenshot).toBe(screenshotOutput); expect(mockExecSync).toHaveBeenCalledWith('adb devices -l', { stdio: 'pipe', timeout: 5000 }); expect(mockExecSync).toHaveBeenCalledWith('adb -s emulator-5554 exec-out screencap -p', { stdio: 'pipe', timeout: 5000, }); }); it('should capture screenshot from first available device if no deviceId specified', () => { const deviceListOutput = `List of devices attached emulator-5554 device product:sdk_gphone_x86`; const screenshotOutput = 'fake-png-data'; // Simulate PNG data as string mockExecSync .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check for getFirstAvailableDevice .mockReturnValueOnce(Buffer.from(deviceListOutput)) // devices -l call .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check for screenshot .mockReturnValueOnce(Buffer.from(screenshotOutput)); // screenshot capture const screenshot = captureScreenshot(); expect(screenshot).toBe(screenshotOutput); expect(mockExecSync).toHaveBeenCalledWith('adb -s emulator-5554 exec-out screencap -p', { stdio: 'pipe', timeout: 5000, }); }); it('should throw DeviceNotFoundError if device does not exist', () => { const deviceListOutput = `List of devices attached emulator-5554 device product:sdk_gphone_x86`; mockExecSync .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check .mockReturnValueOnce(Buffer.from(deviceListOutput)); // devices -l call expect(() => captureScreenshot('nonexistent-device')).toThrow(DeviceNotFoundError); }); it('should throw ADBCommandError if device is not available', () => { const deviceListOutput = `List of devices attached emulator-5554 unauthorized product:sdk_gphone_x86`; mockExecSync .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check .mockReturnValueOnce(Buffer.from(deviceListOutput)); // devices -l call expect(() => captureScreenshot('emulator-5554')).toThrow( "Device 'emulator-5554' is not available" ); }); }); describe('getDeviceInfo', () => { it('should return device information for specified device', () => { const deviceListOutput = `List of devices attached emulator-5554 device product:sdk_gphone_x86 model:sdk_gphone_x86 transport_id:1`; mockExecSync .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check .mockReturnValueOnce(Buffer.from(deviceListOutput)); // devices -l call const deviceInfo = getDeviceInfo('emulator-5554'); expect(deviceInfo).toEqual({ id: 'emulator-5554', status: 'device', product: 'sdk_gphone_x86', model: 'sdk_gphone_x86', transportId: '1', }); }); it('should throw DeviceNotFoundError if device does not exist', () => { const deviceListOutput = `List of devices attached emulator-5554 device product:sdk_gphone_x86`; mockExecSync .mockReturnValueOnce(Buffer.from('Android Debug Bridge version 1.0.41')) // ADB version check .mockReturnValueOnce(Buffer.from(deviceListOutput)); // devices -l call expect(() => getDeviceInfo('nonexistent-device')).toThrow(DeviceNotFoundError); }); }); });

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/infiniV/Android-Ui-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server