fix ci test

This commit is contained in:
LaZzyMan 2026-02-02 20:16:18 +08:00
parent 1050163804
commit 30b4b47cd7

View file

@ -5,19 +5,22 @@
*/
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { execCommand } from '@qwen-code/qwen-code-core';
import {
clipboardHasImage,
saveClipboardImage,
cleanupOldClipboardImages,
} from './clipboardUtils.js';
// Mock execCommand
vi.mock('@qwen-code/qwen-code-core', () => ({
execCommand: vi.fn(),
}));
// Mock ClipboardManager
const mockHasFormat = vi.fn();
const mockGetImageData = vi.fn();
const mockExecCommand = vi.mocked(execCommand);
vi.mock('@teddyzhu/clipboard', () => ({
ClipboardManager: vi.fn().mockImplementation(() => ({
hasFormat: mockHasFormat,
getImageData: mockGetImageData,
})),
}));
describe('clipboardUtils', () => {
beforeEach(() => {
@ -25,319 +28,99 @@ describe('clipboardUtils', () => {
});
describe('clipboardHasImage', () => {
describe('macOS platform', () => {
beforeEach(() => {
vi.stubGlobal('process', {
...process,
platform: 'darwin',
env: process.env,
});
});
it('should return true when clipboard contains image', async () => {
mockHasFormat.mockReturnValue(true);
it('should return true when clipboard contains PNG image', async () => {
mockExecCommand.mockResolvedValue({
stdout: '«class PNGf»',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(true);
expect(mockExecCommand).toHaveBeenCalledWith(
'osascript',
['-e', 'clipboard info'],
{ timeout: 1500 },
);
});
it('should return true when clipboard contains JPEG image', async () => {
mockExecCommand.mockResolvedValue({
stdout: '«class JPEG»',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(true);
});
it('should return true when clipboard contains WebP image', async () => {
mockExecCommand.mockResolvedValue({
stdout: '«class WEBP»',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(true);
});
it('should return true when clipboard contains HEIC image', async () => {
mockExecCommand.mockResolvedValue({
stdout: 'public.heic',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(true);
});
it('should return true when clipboard contains BMP image', async () => {
mockExecCommand.mockResolvedValue({
stdout: '«class BMPf»',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(true);
});
it('should return false when clipboard contains text', async () => {
mockExecCommand.mockResolvedValue({
stdout: '«class utf8»',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(false);
});
it('should return false on error', async () => {
mockExecCommand.mockRejectedValue(new Error('Command failed'));
const result = await clipboardHasImage();
expect(result).toBe(false);
});
const result = await clipboardHasImage();
expect(result).toBe(true);
expect(mockHasFormat).toHaveBeenCalledWith('image');
});
describe('Windows platform', () => {
beforeEach(() => {
vi.stubGlobal('process', {
...process,
platform: 'win32',
env: process.env,
});
});
it('should return false when clipboard does not contain image', async () => {
mockHasFormat.mockReturnValue(false);
it('should return true when clipboard contains image', async () => {
mockExecCommand.mockResolvedValue({
stdout: 'true',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(true);
expect(mockExecCommand).toHaveBeenCalledWith(
'powershell.exe',
expect.arrayContaining([
'-command',
expect.stringContaining('Get-Clipboard'),
]),
);
});
it('should return false when clipboard does not contain image', async () => {
mockExecCommand.mockResolvedValue({
stdout: 'false',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(false);
});
it('should return false when all PowerShell hosts fail', async () => {
mockExecCommand
.mockRejectedValueOnce(new Error('PowerShell not found'))
.mockRejectedValueOnce(new Error('PowerShell not found'))
.mockRejectedValueOnce(new Error('PowerShell not found'));
const result = await clipboardHasImage();
expect(result).toBe(false);
expect(mockExecCommand).toHaveBeenCalledTimes(3);
});
const result = await clipboardHasImage();
expect(result).toBe(false);
expect(mockHasFormat).toHaveBeenCalledWith('image');
});
describe('Linux platform', () => {
beforeEach(() => {
vi.stubGlobal('process', {
...process,
platform: 'linux',
env: process.env,
});
it('should return false on error', async () => {
mockHasFormat.mockImplementation(() => {
throw new Error('Clipboard error');
});
it('should return true when xclip has PNG image', async () => {
// First call: which xclip (success)
// Second call: xclip get PNG (has content)
mockExecCommand
.mockResolvedValueOnce({
stdout: '/usr/bin/xclip',
stderr: '',
code: 0,
})
.mockResolvedValueOnce({ stdout: 'image-data', stderr: '', code: 0 });
const result = await clipboardHasImage();
expect(result).toBe(false);
});
const result = await clipboardHasImage();
expect(result).toBe(true);
it('should log errors in DEBUG mode', async () => {
const originalEnv = process.env;
vi.stubGlobal('process', {
...process,
env: { ...originalEnv, DEBUG: '1' },
});
it('should try multiple formats with xclip', async () => {
// which xclip succeeds
mockExecCommand.mockResolvedValueOnce({
stdout: '/usr/bin/xclip',
stderr: '',
code: 0,
});
// PNG fails
mockExecCommand.mockRejectedValueOnce(new Error('No PNG'));
// JPEG succeeds
mockExecCommand.mockResolvedValueOnce({
stdout: 'jpeg-data',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(true);
const consoleErrorSpy = vi
.spyOn(console, 'error')
.mockImplementation(() => {});
mockHasFormat.mockImplementation(() => {
throw new Error('Test error');
});
it('should fallback to xsel when xclip not available', async () => {
// which xclip fails
mockExecCommand.mockRejectedValueOnce(new Error('xclip not found'));
// which xsel succeeds
mockExecCommand.mockResolvedValueOnce({
stdout: '/usr/bin/xsel',
stderr: '',
code: 0,
});
// xsel -b -t returns image MIME types
mockExecCommand.mockResolvedValueOnce({
stdout: 'text/plain\nimage/png\ntext/html',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(true);
});
it('should fallback to wl-paste when xclip and xsel not available', async () => {
// which xclip fails
mockExecCommand.mockRejectedValueOnce(new Error('xclip not found'));
// which xsel fails
mockExecCommand.mockRejectedValueOnce(new Error('xsel not found'));
// which wl-paste succeeds
mockExecCommand.mockResolvedValueOnce({
stdout: '/usr/bin/wl-paste',
stderr: '',
code: 0,
});
// wl-paste --list-types returns image MIME type
mockExecCommand.mockResolvedValueOnce({
stdout: 'text/plain\nimage/png\ntext/html',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(true);
});
it('should return false when no clipboard tool available', async () => {
// All tools fail
mockExecCommand.mockRejectedValue(new Error('Not found'));
const result = await clipboardHasImage();
expect(result).toBe(false);
});
it('should return false when xsel has no image types', async () => {
// which xclip fails
mockExecCommand.mockRejectedValueOnce(new Error('xclip not found'));
// which xsel succeeds
mockExecCommand.mockResolvedValueOnce({
stdout: '/usr/bin/xsel',
stderr: '',
code: 0,
});
// xsel -b -t returns only text types
mockExecCommand.mockResolvedValueOnce({
stdout: 'text/plain\ntext/html',
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(false);
});
await clipboardHasImage();
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error checking clipboard for image:',
expect.any(Error),
);
consoleErrorSpy.mockRestore();
});
});
describe('saveClipboardImage', () => {
const testTempDir = '/tmp/test-clipboard';
it('should return null when clipboard has no image', async () => {
mockHasFormat.mockReturnValue(false);
it('should create clipboard directory when saving image', async () => {
vi.stubGlobal('process', {
...process,
platform: 'darwin',
env: process.env,
});
const result = await saveClipboardImage('/tmp/test');
expect(result).toBe(null);
});
// Mock all execCommand calls to fail (no image in clipboard)
mockExecCommand.mockRejectedValue(new Error('No image'));
it('should return null when image data buffer is null', async () => {
mockHasFormat.mockReturnValue(true);
mockGetImageData.mockReturnValue({ data: null });
const result = await saveClipboardImage(testTempDir);
// Should return null when no image available
const result = await saveClipboardImage('/tmp/test');
expect(result).toBe(null);
});
it('should handle errors gracefully and return null', async () => {
const result = await saveClipboardImage(
'/invalid/path/that/does/not/exist',
);
mockHasFormat.mockImplementation(() => {
throw new Error('Clipboard error');
});
const result = await saveClipboardImage('/tmp/test');
expect(result).toBe(null);
});
it('should support macOS platform', async () => {
it('should log errors in DEBUG mode', async () => {
const originalEnv = process.env;
vi.stubGlobal('process', {
...process,
platform: 'darwin',
env: process.env,
env: { ...originalEnv, DEBUG: '1' },
});
mockExecCommand.mockRejectedValue(new Error('No image'));
const result = await saveClipboardImage();
expect(result === null || typeof result === 'string').toBe(true);
});
it('should support Windows platform', async () => {
vi.stubGlobal('process', {
...process,
platform: 'win32',
env: process.env,
const consoleErrorSpy = vi
.spyOn(console, 'error')
.mockImplementation(() => {});
mockHasFormat.mockImplementation(() => {
throw new Error('Test error');
});
mockExecCommand.mockRejectedValue(new Error('No image'));
const result = await saveClipboardImage();
expect(result === null || typeof result === 'string').toBe(true);
});
it('should support Linux platform', async () => {
vi.stubGlobal('process', {
...process,
platform: 'linux',
env: process.env,
});
mockExecCommand.mockRejectedValue(new Error('No image'));
const result = await saveClipboardImage();
expect(result === null || typeof result === 'string').toBe(true);
await saveClipboardImage('/tmp/test');
expect(consoleErrorSpy).toHaveBeenCalledWith(
'Error saving clipboard image:',
expect.any(Error),
);
consoleErrorSpy.mockRestore();
});
});
@ -358,84 +141,4 @@ describe('clipboardUtils', () => {
expect(true).toBe(true);
});
});
describe('multi-format support', () => {
beforeEach(() => {
vi.stubGlobal('process', {
...process,
platform: 'darwin',
env: process.env,
});
});
const formats = [
{ name: 'PNG', pattern: '«class PNGf»' },
{ name: 'JPEG', pattern: '«class JPEG»' },
{ name: 'WebP', pattern: '«class WEBP»' },
{ name: 'HEIC', pattern: '«class heic»' },
{ name: 'HEIF', pattern: 'public.heif' },
{ name: 'TIFF', pattern: '«class TIFF»' },
{ name: 'GIF', pattern: '«class GIFf»' },
{ name: 'BMP', pattern: '«class BMPf»' },
];
formats.forEach(({ name, pattern }) => {
it(`should detect ${name} format on macOS`, async () => {
mockExecCommand.mockResolvedValue({
stdout: pattern,
stderr: '',
code: 0,
});
const result = await clipboardHasImage();
expect(result).toBe(true);
});
});
});
describe('error handling with DEBUG mode', () => {
const originalEnv = process.env;
describe('clipboardHasImage', () => {
beforeEach(() => {
vi.stubGlobal('process', {
...process,
platform: 'darwin',
env: { ...originalEnv, DEBUG: '1' },
});
});
it('should log errors in DEBUG mode', async () => {
const consoleErrorSpy = vi
.spyOn(console, 'error')
.mockImplementation(() => {});
mockExecCommand.mockRejectedValue(new Error('Test error'));
await clipboardHasImage();
expect(consoleErrorSpy).toHaveBeenCalled();
consoleErrorSpy.mockRestore();
});
});
describe('saveClipboardImage on Windows', () => {
beforeEach(() => {
vi.stubGlobal('process', {
...process,
platform: 'win32',
env: { ...originalEnv, DEBUG: '1' },
});
});
it('should log errors in DEBUG mode', async () => {
const consoleErrorSpy = vi
.spyOn(console, 'error')
.mockImplementation(() => {});
mockExecCommand.mockRejectedValue(new Error('Test error'));
await saveClipboardImage('/invalid/path');
expect(consoleErrorSpy).toHaveBeenCalled();
consoleErrorSpy.mockRestore();
});
});
});
});