|
9 | 9 | import type { FileTypeResult } from '../../src/utils/file-type-detector'; |
10 | 10 |
|
11 | 11 | // Helper to create a testable version of detectPlatformFromFile logic |
| 12 | +// This mirrors the logic in the actual implementation |
12 | 13 | function determinePlatform( |
| 14 | + filePath: string, |
13 | 15 | fileType: FileTypeResult | undefined, |
14 | 16 | ): 'Android' | 'iOS' | undefined { |
| 17 | + const ext = filePath.split('.').pop()?.toLowerCase() || ''; |
| 18 | + |
| 19 | + // Check for Android APK files via magic bytes |
15 | 20 | if (fileType) { |
16 | 21 | if ( |
17 | 22 | fileType.ext === 'apk' || |
18 | 23 | fileType.mime === 'application/vnd.android.package-archive' |
19 | 24 | ) { |
20 | 25 | return 'Android'; |
21 | 26 | } |
| 27 | + } |
22 | 28 |
|
23 | | - if (fileType.ext === 'zip' || fileType.mime === 'application/zip') { |
24 | | - return 'iOS'; |
25 | | - } |
| 29 | + // Extension-based detection |
| 30 | + if (ext === 'apk' || ext === 'apks') { |
| 31 | + return 'Android'; |
| 32 | + } |
| 33 | + |
| 34 | + if (ext === 'ipa' || ext === 'app') { |
| 35 | + return 'iOS'; |
| 36 | + } |
| 37 | + |
| 38 | + // ZIP files with zip mime type are assumed iOS |
| 39 | + if (ext === 'zip' && fileType?.mime === 'application/zip') { |
| 40 | + return 'iOS'; |
26 | 41 | } |
27 | 42 |
|
28 | 43 | return undefined; |
29 | 44 | } |
30 | 45 |
|
31 | 46 | describe('file-type-detector', () => { |
32 | 47 | describe('platform detection logic', () => { |
33 | | - it('should detect Android for APK files (by ext)', () => { |
34 | | - const result = determinePlatform({ |
35 | | - ext: 'apk', |
36 | | - mime: 'application/vnd.android.package-archive', |
| 48 | + describe('Android detection', () => { |
| 49 | + it('should detect Android for APK files (by magic bytes)', () => { |
| 50 | + const result = determinePlatform('app.apk', { |
| 51 | + ext: 'apk', |
| 52 | + mime: 'application/vnd.android.package-archive', |
| 53 | + }); |
| 54 | + expect(result).toBe('Android'); |
37 | 55 | }); |
38 | | - expect(result).toBe('Android'); |
39 | | - }); |
40 | 56 |
|
41 | | - it('should detect Android for files with android package mime type', () => { |
42 | | - const result = determinePlatform({ |
43 | | - ext: 'unknown', |
44 | | - mime: 'application/vnd.android.package-archive', |
| 57 | + it('should detect Android for files with android package mime type', () => { |
| 58 | + const result = determinePlatform('app.unknown', { |
| 59 | + ext: 'unknown', |
| 60 | + mime: 'application/vnd.android.package-archive', |
| 61 | + }); |
| 62 | + expect(result).toBe('Android'); |
45 | 63 | }); |
46 | | - expect(result).toBe('Android'); |
47 | | - }); |
48 | 64 |
|
49 | | - it('should detect iOS for zip files (by ext)', () => { |
50 | | - const result = determinePlatform({ |
51 | | - ext: 'zip', |
52 | | - mime: 'application/zip', |
| 65 | + it('should detect Android for .apk extension (without magic bytes)', () => { |
| 66 | + const result = determinePlatform('app.apk', undefined); |
| 67 | + expect(result).toBe('Android'); |
53 | 68 | }); |
54 | | - expect(result).toBe('iOS'); |
55 | | - }); |
56 | 69 |
|
57 | | - it('should detect iOS for files with zip mime type', () => { |
58 | | - const result = determinePlatform({ |
59 | | - ext: 'unknown', |
60 | | - mime: 'application/zip', |
| 70 | + it('should detect Android for .apks extension', () => { |
| 71 | + const result = determinePlatform('app.apks', undefined); |
| 72 | + expect(result).toBe('Android'); |
61 | 73 | }); |
62 | | - expect(result).toBe('iOS'); |
63 | | - }); |
64 | 74 |
|
65 | | - it('should return undefined when file type is undefined', () => { |
66 | | - const result = determinePlatform(undefined); |
67 | | - expect(result).toBeUndefined(); |
| 75 | + it('should prioritize apk ext over mime type', () => { |
| 76 | + // Even with a wrong mime type, apk ext should return Android |
| 77 | + const result = determinePlatform('app.apk', { |
| 78 | + ext: 'apk', |
| 79 | + mime: 'application/zip', |
| 80 | + }); |
| 81 | + expect(result).toBe('Android'); |
| 82 | + }); |
68 | 83 | }); |
69 | 84 |
|
70 | | - it('should return undefined for non-mobile file types', () => { |
71 | | - const result = determinePlatform({ |
72 | | - ext: 'pdf', |
73 | | - mime: 'application/pdf', |
| 85 | + describe('iOS detection', () => { |
| 86 | + it('should detect iOS for .ipa extension', () => { |
| 87 | + const result = determinePlatform('app.ipa', { |
| 88 | + ext: 'zip', |
| 89 | + mime: 'application/zip', |
| 90 | + }); |
| 91 | + expect(result).toBe('iOS'); |
74 | 92 | }); |
75 | | - expect(result).toBeUndefined(); |
76 | | - }); |
77 | 93 |
|
78 | | - it('should return undefined for image files', () => { |
79 | | - const result = determinePlatform({ |
80 | | - ext: 'png', |
81 | | - mime: 'image/png', |
| 94 | + it('should detect iOS for .ipa extension (without magic bytes)', () => { |
| 95 | + const result = determinePlatform('app.ipa', undefined); |
| 96 | + expect(result).toBe('iOS'); |
| 97 | + }); |
| 98 | + |
| 99 | + it('should detect iOS for .app extension', () => { |
| 100 | + const result = determinePlatform('MyApp.app', undefined); |
| 101 | + expect(result).toBe('iOS'); |
82 | 102 | }); |
83 | | - expect(result).toBeUndefined(); |
84 | | - }); |
85 | 103 |
|
86 | | - it('should return undefined for text files', () => { |
87 | | - const result = determinePlatform({ |
88 | | - ext: 'txt', |
89 | | - mime: 'text/plain', |
| 104 | + it('should detect iOS for .zip files with zip mime type', () => { |
| 105 | + const result = determinePlatform('app.zip', { |
| 106 | + ext: 'zip', |
| 107 | + mime: 'application/zip', |
| 108 | + }); |
| 109 | + expect(result).toBe('iOS'); |
| 110 | + }); |
| 111 | + |
| 112 | + it('should detect iOS for uppercase IPA extension', () => { |
| 113 | + const result = determinePlatform('app.IPA', undefined); |
| 114 | + expect(result).toBe('iOS'); |
90 | 115 | }); |
91 | | - expect(result).toBeUndefined(); |
92 | 116 | }); |
93 | 117 |
|
94 | | - it('should prioritize apk ext over mime type', () => { |
95 | | - // Even with a wrong mime type, apk ext should return Android |
96 | | - const result = determinePlatform({ |
97 | | - ext: 'apk', |
98 | | - mime: 'application/zip', |
| 118 | + describe('undefined cases', () => { |
| 119 | + it('should return undefined when file type is undefined and no matching extension', () => { |
| 120 | + const result = determinePlatform('file.unknown', undefined); |
| 121 | + expect(result).toBeUndefined(); |
| 122 | + }); |
| 123 | + |
| 124 | + it('should return undefined for non-mobile file types', () => { |
| 125 | + const result = determinePlatform('document.pdf', { |
| 126 | + ext: 'pdf', |
| 127 | + mime: 'application/pdf', |
| 128 | + }); |
| 129 | + expect(result).toBeUndefined(); |
| 130 | + }); |
| 131 | + |
| 132 | + it('should return undefined for image files', () => { |
| 133 | + const result = determinePlatform('image.png', { |
| 134 | + ext: 'png', |
| 135 | + mime: 'image/png', |
| 136 | + }); |
| 137 | + expect(result).toBeUndefined(); |
| 138 | + }); |
| 139 | + |
| 140 | + it('should return undefined for text files', () => { |
| 141 | + const result = determinePlatform('readme.txt', { |
| 142 | + ext: 'txt', |
| 143 | + mime: 'text/plain', |
| 144 | + }); |
| 145 | + expect(result).toBeUndefined(); |
| 146 | + }); |
| 147 | + |
| 148 | + it('should return undefined for .zip without mime type detection', () => { |
| 149 | + // If magic bytes couldn't detect the file type, don't assume iOS |
| 150 | + const result = determinePlatform('flows.zip', undefined); |
| 151 | + expect(result).toBeUndefined(); |
99 | 152 | }); |
100 | | - expect(result).toBe('Android'); |
101 | 153 | }); |
102 | 154 | }); |
103 | 155 | }); |
0 commit comments