import {Intents} from '@shopify/shop-minis-platform' import {renderHook} from '@testing-library/react' import {beforeEach, describe, expect, it, vi} from 'vitest' import {useShopActions} from '../../internal/useShopActions' import {useResolveIntent} from './useResolveIntent' import type {ShopActions} from '@shopify/shop-minis-platform/actions' vi.mock('../../internal/useShopActions', () => ({ useShopActions: vi.fn(), })) const mockUseShopActions = vi.mocked(useShopActions) const mockShopActions = (overrides: Partial) => mockUseShopActions.mockReturnValue(overrides as ShopActions) describe('useResolveIntent', () => { beforeEach(() => { vi.clearAllMocks() }) it('forwards intentKey + ok result with data through the bridge', () => { const mockResolve = vi.fn().mockResolvedValue({ok: true, data: undefined}) mockShopActions({resolveIntent: mockResolve}) const {result} = renderHook(() => useResolveIntent('tryOnProduct')) result.current.resolveIntent({ code: 'ok', data: {imageUrl: 'https://cdn.shopify.com/tryon/abc.jpg'}, }) expect(mockResolve).toHaveBeenCalledWith({ intentKey: 'tryOnProduct', result: { code: 'ok', data: {imageUrl: 'https://cdn.shopify.com/tryon/abc.jpg'}, }, }) }) it('forwards a closed result', () => { const mockResolve = vi.fn().mockResolvedValue({ok: true, data: undefined}) mockShopActions({resolveIntent: mockResolve}) const {result} = renderHook(() => useResolveIntent('tryOnProduct')) result.current.resolveIntent({code: 'closed'}) expect(mockResolve).toHaveBeenCalledWith({ intentKey: 'tryOnProduct', result: {code: 'closed'}, }) }) it('forwards an error result with message', () => { const mockResolve = vi.fn().mockResolvedValue({ok: true, data: undefined}) mockShopActions({resolveIntent: mockResolve}) const {result} = renderHook(() => useResolveIntent('tryOnProduct')) result.current.resolveIntent({code: 'error', message: 'Upload failed'}) expect(mockResolve).toHaveBeenCalledWith({ intentKey: 'tryOnProduct', result: {code: 'error', message: 'Upload failed'}, }) }) it('accepts the Intents enum object alongside string literals', () => { const mockResolve = vi.fn().mockResolvedValue({ok: true, data: undefined}) mockShopActions({resolveIntent: mockResolve}) const {result} = renderHook(() => useResolveIntent(Intents.TryOnProduct)) result.current.resolveIntent({code: 'closed'}) expect(mockResolve).toHaveBeenCalledWith({ intentKey: 'tryOnProduct', result: {code: 'closed'}, }) }) it('returns a stable resolveIntent reference across rerenders', () => { const mockResolve = vi.fn().mockResolvedValue({ok: true, data: undefined}) mockShopActions({resolveIntent: mockResolve}) const {result, rerender} = renderHook(() => useResolveIntent('tryOnProduct') ) const first = result.current.resolveIntent rerender() expect(result.current.resolveIntent).toBe(first) }) it('returns the promise from the bridge action', async () => { const mockResolve = vi.fn().mockResolvedValue({ok: true, data: undefined}) mockShopActions({resolveIntent: mockResolve}) const {result} = renderHook(() => useResolveIntent('tryOnProduct')) await expect(result.current.resolveIntent({code: 'closed'})).resolves.toBe( undefined ) }) it('rejects when the bridge action returns an error result', async () => { const error = new Error('Resolve failed') const mockResolve = vi.fn().mockResolvedValue({ok: false, error}) mockShopActions({resolveIntent: mockResolve}) const {result} = renderHook(() => useResolveIntent('tryOnProduct')) await expect(result.current.resolveIntent({code: 'closed'})).rejects.toBe( error ) }) describe('type safety (compile-time)', () => { it('rejects calls without an intentKey argument', () => { mockShopActions({ resolveIntent: vi.fn().mockResolvedValue({ok: true, data: undefined}), }) // @ts-expect-error — intentKey is a required positional arg renderHook(() => useResolveIntent()) }) it('rejects an unknown intentKey', () => { mockShopActions({ resolveIntent: vi.fn().mockResolvedValue({ok: true, data: undefined}), }) // @ts-expect-error — 'notARegisteredIntent' is not in IntentDefinitions renderHook(() => useResolveIntent('notARegisteredIntent')) }) it('rejects result.data shapes that do not match the declared intent', () => { const mockResolve = vi.fn().mockResolvedValue({ok: true, data: undefined}) mockShopActions({resolveIntent: mockResolve}) const {result} = renderHook(() => useResolveIntent('tryOnProduct')) result.current.resolveIntent({ code: 'ok', // @ts-expect-error — TryOnProduct result expects {imageUrl: string} data: {variantId: 'gid://shopify/ProductVariant/9'}, }) }) it('requires result.data for intents with a declared response type', () => { const mockResolve = vi.fn().mockResolvedValue({ok: true, data: undefined}) mockShopActions({resolveIntent: mockResolve}) const {result} = renderHook(() => useResolveIntent('tryOnProduct')) // @ts-expect-error — TryOnProduct declares a response type, so `data` is required result.current.resolveIntent({code: 'ok'}) }) }) })