/** @vitest-environment jsdom */ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'; const common = vi.hoisted(() => ({ composeShareImage: vi.fn(), copyText: vi.fn(), detectShareCapability: vi.fn(), downloadShareImage: vi.fn(), openShareSheet: vi.fn(), shareFile: vi.fn(), })); vi.mock('../share', () => common); import { shareTryOn, tryOnFilename } from './share'; const raw = 'data:image/png;base64,RAW_RESULT'; const branded = new Blob(['BRANDED_STORY'], { type: 'image/png' }); const request = { image: raw, filename: 'virtual-tryon-shirt.png', title: 'Linen Shirt', url: 'https://shop.example/products/linen-shirt?variation_id=7', merchantName: 'Shop Example', productImage: 'shop.example', }; beforeEach(() => { vi.clearAllMocks(); common.composeShareImage.mockResolvedValue(branded); common.shareFile.mockReturnValue(new File([branded], 'story.png', { type: 'image/png' })); common.detectShareCapability.mockReturnValue('file_share'); common.openShareSheet.mockResolvedValue({ capability: 'file_share', outcome: 'share_sheet_closed', payloadMode: 'file_text' }); common.copyText.mockResolvedValue(true); }); afterEach(() => vi.restoreAllMocks()); describe('sharing a saved try-on', () => { it('composes before handoff and never gives the raw gallery result to share/download', async () => { expect(await shareTryOn(request)).toBe('shared'); expect(common.composeShareImage).toHaveBeenCalledWith(raw, expect.objectContaining({ itemName: 'Linen Shirt', productUrl: request.url, brandName: 'Shop Example', productImage: 'shop.example', })); expect(common.shareFile).toHaveBeenCalledWith(branded); expect(common.openShareSheet).toHaveBeenCalledWith(expect.any(File), expect.objectContaining({ productUrl: request.url })); expect(common.shareFile).not.toHaveBeenCalledWith(raw, expect.anything()); }); it('treats native cancellation honestly and does not download the raw result', async () => { common.openShareSheet.mockResolvedValue({ capability: 'file_share', outcome: 'cancelled', payloadMode: 'file_only' }); expect(await shareTryOn(request)).toBe('dismissed'); expect(common.downloadShareImage).not.toHaveBeenCalled(); }); it('downloads the branded composition and copies the exact link when file sharing is unavailable', async () => { common.detectShareCapability.mockReturnValue('download_clipboard'); expect(await shareTryOn(request)).toBe('downloaded'); expect(common.downloadShareImage).toHaveBeenCalledWith(branded); expect(common.copyText).toHaveBeenCalledWith(request.url); expect(common.downloadShareImage).not.toHaveBeenCalledWith(raw, expect.anything()); }); it('fails closed instead of exporting raw pixels when composition fails', async () => { common.composeShareImage.mockRejectedValue(new Error('tainted')); expect(await shareTryOn(request)).toBe('failed'); expect(common.shareFile).not.toHaveBeenCalled(); expect(common.downloadShareImage).not.toHaveBeenCalled(); }); }); describe('tryOnFilename', () => { it('slugifies product names and always returns a local PNG name', () => { expect(tryOnFilename('Linen Shirt', 1234)).toBe('virtual-tryon-linen-shirt-1234.png'); expect(tryOnFilename('Maglietta à righe (blu)!', 1234)).toBe('virtual-tryon-maglietta-righe-blu-1234.png'); expect(tryOnFilename(undefined, 1234)).toBe('virtual-tryon-result-1234.png'); }); });