/** * 测试计划: * 1. Image 类别继续兼容所有图片 MIME 类型。 * 2. accept 支持扩展名、完整 MIME 类型和 MIME 通配符。 * 3. accept 支持全量通配符与 jpg、jpeg 扩展名互认。 * 4. 未配置 accept 时不限制,非法 token 不放宽有效规则。 * 5. 文件不匹配任何 accept 配置时拒绝上传。 * 6. 扩展名命中但声明 MIME 冲突时拒绝上传。 * 7. 扩展名和 MIME 即使分别命中不同 accept token,也不能绕过一致性校验。 */ import { PhoneOS, UploadFileTypes } from '../enum'; import { getAcceptDocumentTypes, getAcceptMimeTypes, isFileMatchAccept, isFileMatchType, validateFilesByAccept, validateFilesByTypes, } from '../help'; import type { ImageInfo } from '../interface'; const createFile = ( file: Partial> = {} ): ImageInfo => ({ uri: 'file:///tmp/file', ...file, }); describe('Uploader file type validation', () => { test.each([ [PhoneOS.Android, ['image/jpeg', 'image/png']], [PhoneOS.Ios, []], [PhoneOS.Harmony, ['.jpg', '.jpeg', '.png']], ])('converts accept to %s document picker types', (platform, expected) => { expect(getAcceptDocumentTypes('.jpg,.png', platform)).toEqual(expected); }); test.each([ ['image/jpeg,image/png', ['.jpg', '.jpeg', '.png']], ['image/*', []], ['*', []], ['.png,invalid', ['.png']], ])('converts Harmony accept %s to extensions', (accept, expected) => { expect(getAcceptDocumentTypes(accept, PhoneOS.Harmony)).toEqual(expected); }); test.each([ [undefined, []], ['*', []], ['invalid', []], ['.jpg,.jpeg,.png', ['image/jpeg', 'image/png']], ['.jpg,foo.bar', ['image/jpeg']], ['image/*', ['image/*']], ])('converts accept %s to native MIME types', (accept, expected) => { expect(getAcceptMimeTypes(accept)).toEqual(expected); }); test.each([ ['photo.gif', 'image/gif'], ['photo.heic', 'image/heic'], ['photo.avif', 'image/avif'], ])('keeps Image compatible with %s (%s)', (fileName, type) => { expect( isFileMatchType(createFile({ fileName, type }), UploadFileTypes.Image) ).toBe(true); }); test.each([ ['PHOTO.JPG', undefined, '.jpg,.png'], ['photo', 'image/jpeg', 'image/jpeg'], ['photo.png', 'image/png', 'image/*'], ['report.PDF', undefined, ' .jpg, .pdf '], ['photo.jpeg', undefined, 'image/jpeg'], ['photo.jpg', 'image/jpg', 'image/jpeg'], ['photo.gif', 'image/gif', '*'], ['report.pdf', 'application/pdf', '*/*'], ['payload.jpg', 'application/zip', '*'], ['payload.jpg', 'application/zip', '*/*'], ['photo.jpeg', 'image/jpeg', '.jpg'], ['photo.jpg', 'image/jpeg', '.jpeg'], ['photo.jpg', 'image/jpeg', '.jpg,foo.bar'], ])('matches %s (%s) against accept %s', (fileName, type, accept) => { expect(isFileMatchAccept(createFile({ fileName, type }), accept)).toBe( true ); }); test.each([ ['photo.gif', 'image/gif', '.jpg,.png'], ['report.pdf', 'application/pdf', 'image/*'], ['photo.png', 'image/png', 'image/jpeg'], ['archive.zip', 'application/zip', '.pdf,application/pdf'], ['archive.zip', 'application/zip', 'invalid'], ['report.pdf', 'application/pdf', '.jpg,foo.bar'], ])('rejects %s (%s) against accept %s', (fileName, type, accept) => { expect(isFileMatchAccept(createFile({ fileName, type }), accept)).toBe( false ); }); test('rejects a matching extension when the declared MIME conflicts', () => { expect( isFileMatchAccept( createFile({ fileName: 'payload.jpg', type: 'application/zip' }), '.jpg' ) ).toBe(false); }); test('checks MIME consistency when the extension comes from the URI', () => { expect( isFileMatchAccept( createFile({ fileName: 'payload', uri: 'file:///tmp/payload.jpg', type: 'application/zip', }), '.jpg' ) ).toBe(false); }); test('rejects conflicting extension and MIME matched by separate tokens', () => { expect( isFileMatchAccept( createFile({ fileName: 'photo.jpg', type: 'image/png' }), '.jpg,image/png' ) ).toBe(false); }); test('keeps an omitted accept unrestricted', () => { const files = [createFile({ fileName: 'photo.heic', type: 'image/heic' })]; expect(validateFilesByAccept(files)).toEqual({ ok: true, allowedLabel: '', }); expect(validateFilesByTypes(files, [UploadFileTypes.AllFile])).toEqual({ ok: true, allowedLabel: '', }); }); test('returns the configured accept value when validation fails', () => { expect( validateFilesByAccept( [createFile({ fileName: 'photo.gif', type: 'image/gif' })], '.jpg,.png,image/jpeg' ) ).toEqual({ ok: false, allowedLabel: '.jpg,.png,image/jpeg', }); }); });