mirror of
https://github.com/bitsocialnet/5chan.git
synced 2026-08-03 07:41:04 +02:00
fix(media hosting): restore android imgur support
This commit is contained in:
@@ -40,7 +40,7 @@ public class ImgurLiveUploadTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@Ignore("Imgur upload is no longer supported on Android WebView; keep for manual diagnostics.")
|
@Ignore("Live provider diagnostic; may fail when Imgur is unavailable from the current network.")
|
||||||
public void imgur_liveUpload_fromGeneratedPng_succeeds() throws Exception {
|
public void imgur_liveUpload_fromGeneratedPng_succeeds() throws Exception {
|
||||||
Intent launchIntent = new Intent(appContext, MainActivity.class);
|
Intent launchIntent = new Intent(appContext, MainActivity.class);
|
||||||
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
launchIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||||
|
|||||||
+3
-3
@@ -18,10 +18,10 @@ import org.junit.runner.RunWith;
|
|||||||
* Instrumentation tests for MediaUploadAutomationRunner against controlled HTML fixtures.
|
* Instrumentation tests for MediaUploadAutomationRunner against controlled HTML fixtures.
|
||||||
* Uses DataTransfer injection (no chooser). Simulates: delayed DOM, missing selectors,
|
* Uses DataTransfer injection (no chooser). Simulates: delayed DOM, missing selectors,
|
||||||
* success URL extraction, blocked. Validates timeout/error classification (input_not_found,
|
* success URL extraction, blocked. Validates timeout/error classification (input_not_found,
|
||||||
* blocked/captcha, upload_timed_out). Kept for opt-in diagnostics because Android app uploads no
|
* blocked/captcha, upload_timed_out). Kept opt-in because these instrumentation tests exercise
|
||||||
* longer use this WebView path.
|
* WebView timing behavior rather than ordinary unit logic.
|
||||||
*/
|
*/
|
||||||
@Ignore("Imgur WebView upload is no longer supported on Android; keep for manual diagnostics.")
|
@Ignore("WebView automation fixture diagnostics; run manually with connected Android tests.")
|
||||||
@RunWith(AndroidJUnit4.class)
|
@RunWith(AndroidJUnit4.class)
|
||||||
public class MediaUploadAutomationRunnerTest {
|
public class MediaUploadAutomationRunnerTest {
|
||||||
|
|
||||||
|
|||||||
@@ -5,9 +5,9 @@ package fivechan.android;
|
|||||||
* Reconciled with electron/media-upload-recipes.js for imgbb and imgur.
|
* Reconciled with electron/media-upload-recipes.js for imgbb and imgur.
|
||||||
*
|
*
|
||||||
* Android vs Electron (prevent drift):
|
* Android vs Electron (prevent drift):
|
||||||
* - Android app uploads use native catbox and WebView automation for imgbb. The imgur runner
|
* - Android app uploads use native catbox and WebView automation for imgur/imgbb. Real Uri
|
||||||
* is retained for diagnostics/tests only. Real Uri attempts use the WebView file chooser
|
* attempts use the WebView file chooser callback when possible; fixtures/fallback use
|
||||||
* callback; fixtures/fallback use DataTransfer JS injection.
|
* DataTransfer JS injection.
|
||||||
* - Electron: catbox/imgur/imgbb. CDP DOM.setFileInputFiles + submit click.
|
* - Electron: catbox/imgur/imgbb. CDP DOM.setFileInputFiles + submit click.
|
||||||
* - Selectors: file input, submit, success extractor, blocked indicators kept in sync for imgbb
|
* - Selectors: file input, submit, success extractor, blocked indicators kept in sync for imgbb
|
||||||
* and imgur.
|
* and imgur.
|
||||||
@@ -36,7 +36,7 @@ public final class MediaUploadRecipes {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public static boolean isAndroidUploadProvider(String provider) {
|
public static boolean isAndroidUploadProvider(String provider) {
|
||||||
return PROVIDER_IMGBB.equals(provider);
|
return PROVIDER_IMGUR.equals(provider) || PROVIDER_IMGBB.equals(provider);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static String getUploadUrl(String provider) {
|
public static String getUploadUrl(String provider) {
|
||||||
|
|||||||
+3
-3
@@ -107,18 +107,18 @@ describe('MediaHostingSettings', () => {
|
|||||||
expect(mockSetUploadMode).toHaveBeenCalledWith('none');
|
expect(mockSetUploadMode).toHaveBeenCalledWith('none');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('disables unsupported providers on Android', async () => {
|
it('enables Imgur on Android', async () => {
|
||||||
uploadModeRef.value = 'preferred';
|
uploadModeRef.value = 'preferred';
|
||||||
preferredProviderRef.value = 'catbox';
|
preferredProviderRef.value = 'catbox';
|
||||||
mockGetPlatform.mockReturnValue('android');
|
mockGetPlatform.mockReturnValue('android');
|
||||||
render();
|
render();
|
||||||
const imgurRadio = container.querySelector<HTMLInputElement>('input[value="imgur"]');
|
const imgurRadio = container.querySelector<HTMLInputElement>('input[value="imgur"]');
|
||||||
expect(imgurRadio).not.toBeNull();
|
expect(imgurRadio).not.toBeNull();
|
||||||
expect(imgurRadio?.disabled).toBe(true);
|
expect(imgurRadio?.disabled).toBe(false);
|
||||||
await act(async () => {
|
await act(async () => {
|
||||||
imgurRadio?.click();
|
imgurRadio?.click();
|
||||||
});
|
});
|
||||||
expect(mockSetPreferredProvider).not.toHaveBeenCalled();
|
expect(mockSetPreferredProvider).toHaveBeenCalledWith('imgur');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('clicking supported provider in preferred mode calls setPreferredProvider', async () => {
|
it('clicking supported provider in preferred mode calls setPreferredProvider', async () => {
|
||||||
|
|||||||
@@ -44,8 +44,8 @@ vi.mock('../../lib/media-hosting/provider-order', () => ({
|
|||||||
if (opts.mode === 'none') return [];
|
if (opts.mode === 'none') return [];
|
||||||
if (opts.runtime === 'web') return filterAvailable(opts.preferredProvider === 'catbox' ? ['catbox'] : []);
|
if (opts.runtime === 'web') return filterAvailable(opts.preferredProvider === 'catbox' ? ['catbox'] : []);
|
||||||
if (opts.runtime === 'android') {
|
if (opts.runtime === 'android') {
|
||||||
if (opts.mode === 'preferred') return filterAvailable(opts.preferredProvider === 'catbox' || opts.preferredProvider === 'imgbb' ? [opts.preferredProvider] : []);
|
if (opts.mode === 'preferred') return filterAvailable(['catbox', 'imgur', 'imgbb'].includes(opts.preferredProvider) ? [opts.preferredProvider] : []);
|
||||||
return filterAvailable(['catbox', 'imgbb']);
|
return filterAvailable(['catbox', 'imgur', 'imgbb']);
|
||||||
}
|
}
|
||||||
return filterAvailable(opts.mode === 'preferred' ? [opts.preferredProvider] : ['catbox', 'imgur', 'imgbb']);
|
return filterAvailable(opts.mode === 'preferred' ? [opts.preferredProvider] : ['catbox', 'imgur', 'imgbb']);
|
||||||
}),
|
}),
|
||||||
@@ -332,9 +332,10 @@ describe('useFileUpload', () => {
|
|||||||
expect(hook().isUploading).toBe(false);
|
expect(hook().isUploading).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('does not call Android plugin when preferred provider is unsupported', async () => {
|
it('does not call Android plugin when preferred Imgur is unavailable from this network', async () => {
|
||||||
uploadModeRef.value = 'preferred';
|
uploadModeRef.value = 'preferred';
|
||||||
preferredProviderRef.value = 'imgur';
|
preferredProviderRef.value = 'imgur';
|
||||||
|
providerAvailabilityRef.value = { imgur: 'unavailable' };
|
||||||
vi.mocked(Capacitor.getPlatform).mockReturnValue('android');
|
vi.mocked(Capacitor.getPlatform).mockReturnValue('android');
|
||||||
const { onUploadComplete, hook } = mountHook();
|
const { onUploadComplete, hook } = mountHook();
|
||||||
|
|
||||||
@@ -343,7 +344,7 @@ describe('useFileUpload', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(FileUploader.pickAndUploadMedia).not.toHaveBeenCalled();
|
expect(FileUploader.pickAndUploadMedia).not.toHaveBeenCalled();
|
||||||
expect(window.alert).toHaveBeenCalledWith('upload_failed: imgur is not supported on android. upload_failed_preferred_guidance');
|
expect(window.alert).toHaveBeenCalledWith('upload_failed: imgur is unavailable from this network. upload_failed_preferred_guidance');
|
||||||
expect(onUploadComplete).not.toHaveBeenCalled();
|
expect(onUploadComplete).not.toHaveBeenCalled();
|
||||||
expect(hook().isUploading).toBe(false);
|
expect(hook().isUploading).toBe(false);
|
||||||
});
|
});
|
||||||
@@ -368,7 +369,7 @@ describe('useFileUpload', () => {
|
|||||||
it('does not call Android plugin when random mode has no reachable providers', async () => {
|
it('does not call Android plugin when random mode has no reachable providers', async () => {
|
||||||
uploadModeRef.value = 'random';
|
uploadModeRef.value = 'random';
|
||||||
preferredProviderRef.value = 'catbox';
|
preferredProviderRef.value = 'catbox';
|
||||||
providerAvailabilityRef.value = { catbox: 'unavailable', imgbb: 'unavailable' };
|
providerAvailabilityRef.value = { catbox: 'unavailable', imgur: 'unavailable', imgbb: 'unavailable' };
|
||||||
vi.mocked(Capacitor.getPlatform).mockReturnValue('android');
|
vi.mocked(Capacitor.getPlatform).mockReturnValue('android');
|
||||||
const { onUploadComplete, hook } = mountHook();
|
const { onUploadComplete, hook } = mountHook();
|
||||||
|
|
||||||
@@ -401,6 +402,25 @@ describe('useFileUpload', () => {
|
|||||||
expect(onUploadComplete).toHaveBeenCalledWith('https://i.ibb.co/example/android.png', 'android.png');
|
expect(onUploadComplete).toHaveBeenCalledWith('https://i.ibb.co/example/android.png', 'android.png');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('calls Android plugin with only Imgur when Imgur is chosen and reachable', async () => {
|
||||||
|
uploadModeRef.value = 'preferred';
|
||||||
|
preferredProviderRef.value = 'imgur';
|
||||||
|
vi.mocked(Capacitor.getPlatform).mockReturnValue('android');
|
||||||
|
vi.mocked(FileUploader.pickAndUploadMedia).mockResolvedValue({
|
||||||
|
url: 'https://i.imgur.com/example.png',
|
||||||
|
fileName: 'android.png',
|
||||||
|
provider: 'imgur',
|
||||||
|
});
|
||||||
|
const { onUploadComplete, hook } = mountHook();
|
||||||
|
|
||||||
|
await act(async () => {
|
||||||
|
await hook().handleUpload();
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(FileUploader.pickAndUploadMedia).toHaveBeenCalledWith({ providerOrder: ['imgur'] });
|
||||||
|
expect(onUploadComplete).toHaveBeenCalledWith('https://i.imgur.com/example.png', 'android.png');
|
||||||
|
});
|
||||||
|
|
||||||
it('Android call includes provider order payload', async () => {
|
it('Android call includes provider order payload', async () => {
|
||||||
uploadModeRef.value = 'random';
|
uploadModeRef.value = 'random';
|
||||||
preferredProviderRef.value = 'catbox';
|
preferredProviderRef.value = 'catbox';
|
||||||
@@ -422,6 +442,7 @@ describe('useFileUpload', () => {
|
|||||||
});
|
});
|
||||||
const call = vi.mocked(FileUploader.pickAndUploadMedia).mock.calls[0][0];
|
const call = vi.mocked(FileUploader.pickAndUploadMedia).mock.calls[0][0];
|
||||||
expect(call?.providerOrder).toEqual(expect.arrayContaining(['catbox']));
|
expect(call?.providerOrder).toEqual(expect.arrayContaining(['catbox']));
|
||||||
|
expect(call?.providerOrder).toEqual(expect.arrayContaining(['imgur']));
|
||||||
expect(call?.providerOrder).toEqual(expect.arrayContaining(['imgbb']));
|
expect(call?.providerOrder).toEqual(expect.arrayContaining(['imgbb']));
|
||||||
expect(onUploadComplete).toHaveBeenCalledWith('https://files.catbox.moe/random.jpg', 'random.jpg');
|
expect(onUploadComplete).toHaveBeenCalledWith('https://files.catbox.moe/random.jpg', 'random.jpg');
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ describe('provider-availability', () => {
|
|||||||
|
|
||||||
expect(snapshot.catbox).toBe('available');
|
expect(snapshot.catbox).toBe('available');
|
||||||
expect(snapshot.imgbb).toBe('available');
|
expect(snapshot.imgbb).toBe('available');
|
||||||
expect(snapshot.imgur).toBe('unknown');
|
expect(snapshot.imgur).toBe('available');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('marks a provider unavailable when one of its media probes fails', async () => {
|
it('marks a provider unavailable when one of its media probes fails', async () => {
|
||||||
|
|||||||
@@ -55,6 +55,13 @@ describe('provider-order', () => {
|
|||||||
runtime: 'web',
|
runtime: 'web',
|
||||||
}),
|
}),
|
||||||
).toEqual(['catbox']);
|
).toEqual(['catbox']);
|
||||||
|
expect(
|
||||||
|
getProviderOrder({
|
||||||
|
mode: 'preferred',
|
||||||
|
preferredProvider: 'imgur',
|
||||||
|
runtime: 'android',
|
||||||
|
}),
|
||||||
|
).toEqual(['imgur']);
|
||||||
expect(
|
expect(
|
||||||
getProviderOrder({
|
getProviderOrder({
|
||||||
mode: 'preferred',
|
mode: 'preferred',
|
||||||
@@ -79,13 +86,6 @@ describe('provider-order', () => {
|
|||||||
runtime: 'web',
|
runtime: 'web',
|
||||||
}),
|
}),
|
||||||
).toEqual([]);
|
).toEqual([]);
|
||||||
expect(
|
|
||||||
getProviderOrder({
|
|
||||||
mode: 'preferred',
|
|
||||||
preferredProvider: 'imgur',
|
|
||||||
runtime: 'android',
|
|
||||||
}),
|
|
||||||
).toEqual([]);
|
|
||||||
expect(
|
expect(
|
||||||
getProviderOrder({
|
getProviderOrder({
|
||||||
mode: 'preferred',
|
mode: 'preferred',
|
||||||
@@ -137,14 +137,14 @@ describe('provider-order', () => {
|
|||||||
expect(order).toEqual(['catbox']);
|
expect(order).toEqual(['catbox']);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('uses catbox and imgbb for android random mode', () => {
|
it('uses catbox, imgur, and imgbb for android random mode', () => {
|
||||||
const order = getProviderOrder({
|
const order = getProviderOrder({
|
||||||
mode: 'random',
|
mode: 'random',
|
||||||
preferredProvider: 'catbox',
|
preferredProvider: 'catbox',
|
||||||
runtime: 'android',
|
runtime: 'android',
|
||||||
});
|
});
|
||||||
expect(order).toHaveLength(2);
|
expect(order).toHaveLength(3);
|
||||||
expect([...order].sort()).toEqual(['catbox', 'imgbb']);
|
expect([...order].sort()).toEqual(['catbox', 'imgbb', 'imgur']);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ export const MEDIA_HOSTING_PROVIDERS: readonly ProviderDefinition[] = [
|
|||||||
label: 'Imgur',
|
label: 'Imgur',
|
||||||
homepageUrl: 'https://imgur.com',
|
homepageUrl: 'https://imgur.com',
|
||||||
availabilityProbeUrls: ['https://s.imgur.com/images/favicon-32x32.png', 'https://i.imgur.com/YpB7qfa.jpg'],
|
availabilityProbeUrls: ['https://s.imgur.com/images/favicon-32x32.png', 'https://i.imgur.com/YpB7qfa.jpg'],
|
||||||
supportedRuntimes: ['electron'],
|
supportedRuntimes: ['electron', 'android'],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
id: 'imgbb',
|
id: 'imgbb',
|
||||||
|
|||||||
@@ -28,6 +28,14 @@ describe('useMediaHostingStore', () => {
|
|||||||
availabilityProbeUrls: ['https://catbox.moe/pictures/logo.png', 'https://files.catbox.moe/8ten4y.png'],
|
availabilityProbeUrls: ['https://catbox.moe/pictures/logo.png', 'https://files.catbox.moe/8ten4y.png'],
|
||||||
supportedRuntimes: ['web', 'electron', 'android'],
|
supportedRuntimes: ['web', 'electron', 'android'],
|
||||||
});
|
});
|
||||||
|
const imgur = MEDIA_HOSTING_PROVIDERS.find((p) => p.id === 'imgur');
|
||||||
|
expect(imgur).toEqual({
|
||||||
|
id: 'imgur',
|
||||||
|
label: 'Imgur',
|
||||||
|
homepageUrl: 'https://imgur.com',
|
||||||
|
availabilityProbeUrls: ['https://s.imgur.com/images/favicon-32x32.png', 'https://i.imgur.com/YpB7qfa.jpg'],
|
||||||
|
supportedRuntimes: ['electron', 'android'],
|
||||||
|
});
|
||||||
const imgbb = MEDIA_HOSTING_PROVIDERS.find((p) => p.id === 'imgbb');
|
const imgbb = MEDIA_HOSTING_PROVIDERS.find((p) => p.id === 'imgbb');
|
||||||
expect(imgbb).toEqual({
|
expect(imgbb).toEqual({
|
||||||
id: 'imgbb',
|
id: 'imgbb',
|
||||||
|
|||||||
Reference in New Issue
Block a user