fix(upload): resolve electron file-path fallback and android provider automation stalls

This commit is contained in:
plebeius
2026-02-19 13:18:15 +08:00
parent afdf129c3e
commit 410b988fbd
6 changed files with 126 additions and 4 deletions
+1
View File
@@ -13,6 +13,7 @@ declare global {
copyToClipboard: (text: string) => Promise<{ success: boolean; error?: string }>;
getPlatform: () => Promise<{ platform: NodeJS.Platform; arch: string; version: string }>;
automateUploadMedia: (options: { provider: ProviderId; filePath: string }) => Promise<{ url: string; provider: ProviderId }>;
getPathForFile?: (file: File) => string | null;
};
}
}
@@ -0,0 +1,70 @@
import { beforeEach, describe, expect, it, vi } from 'vitest';
import { orchestrateElectronUpload } from '../upload-orchestrator';
import { uploadToCatbox } from '../../utils/catbox-utils';
vi.mock('../../utils/catbox-utils', () => ({
uploadToCatbox: vi.fn(),
}));
function createElectronApiMock() {
return {
isElectron: true,
copyToClipboard: vi.fn(async () => ({ success: true })),
getPlatform: vi.fn(async () => ({ platform: 'darwin' as NodeJS.Platform, arch: 'x64', version: 'v20.0.0' })),
automateUploadMedia: vi.fn(async () => ({ url: 'https://i.imgur.com/abc.png', provider: 'imgur' as const })),
getPathForFile: vi.fn((): string | null => '/tmp/image.png'),
};
}
describe('orchestrateElectronUpload', () => {
beforeEach(() => {
vi.clearAllMocks();
window.electronApi = undefined;
window.isElectron = false;
});
it('uploads via catbox provider directly', async () => {
vi.mocked(uploadToCatbox).mockResolvedValue('https://files.catbox.moe/a.png');
const file = new File(['a'], 'a.png', { type: 'image/png' });
const url = await orchestrateElectronUpload(file, ['catbox']);
expect(url).toBe('https://files.catbox.moe/a.png');
expect(uploadToCatbox).toHaveBeenCalledWith(file);
});
it('uses electronApi.getPathForFile when File.path is unavailable', async () => {
const electronApi = createElectronApiMock();
window.electronApi = electronApi;
const file = new File(['x'], 'x.png', { type: 'image/png' });
const url = await orchestrateElectronUpload(file, ['imgur']);
expect(url).toBe('https://i.imgur.com/abc.png');
expect(electronApi.getPathForFile).toHaveBeenCalledWith(file);
expect(electronApi.automateUploadMedia).toHaveBeenCalledWith({
provider: 'imgur',
filePath: '/tmp/image.png',
});
});
it('fails with provider attempt details if no file path can be resolved', async () => {
const electronApi = createElectronApiMock();
electronApi.getPathForFile = vi.fn((): string | null => null);
window.electronApi = electronApi;
const file = new File(['z'], 'z.png', { type: 'image/png' });
try {
await orchestrateElectronUpload(file, ['postimages']);
throw new Error('Expected orchestrateElectronUpload to throw');
} catch (error) {
const typedError = error as Error & {
attempts?: Array<{ provider: string; error?: string }>;
};
expect(typedError.message).toBe('All providers failed');
expect(typedError.attempts?.[0]?.provider).toBe('postimages');
expect(typedError.attempts?.[0]?.error).toContain('File path required for Electron automation');
}
});
});
+18 -1
View File
@@ -8,6 +8,23 @@ export interface OrchestratorAttempt {
error?: string;
}
function resolveElectronFilePath(file: File): string | null {
const fileWithPath = file as File & { path?: string };
if (typeof fileWithPath.path === 'string' && fileWithPath.path.length > 0) {
return fileWithPath.path;
}
const getPathForFile = window.electronApi?.getPathForFile;
if (typeof getPathForFile === 'function') {
const resolvedPath = getPathForFile(file);
if (typeof resolvedPath === 'string' && resolvedPath.length > 0) {
return resolvedPath;
}
}
return null;
}
/**
* Uploads a file via a single provider. Catbox uses the web API;
* imgur/postimages use Electron automation when available.
@@ -17,7 +34,7 @@ async function uploadViaProvider(provider: ProviderId, file: File): Promise<stri
if (provider === 'imgur' || provider === 'postimages') {
const fn = typeof window !== 'undefined' && window.electronApi?.automateUploadMedia;
if (fn) {
const filePath = (file as File & { path?: string }).path;
const filePath = resolveElectronFilePath(file);
if (!filePath) throw new Error('File path required for Electron automation');
const { url } = await fn({ provider, filePath });
return url;