import {renderHook} from '@testing-library/react' import {describe, expect, it, vi, beforeEach} from 'vitest' import {useIntent} from './useIntent' vi.mock('../navigation/useDeeplink', () => ({ useDeeplink: vi.fn(), })) const {useDeeplink} = await import('../navigation/useDeeplink') describe('useIntent', () => { beforeEach(() => { vi.clearAllMocks() }) describe('without intent params', () => { it('returns nulls when no query params', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: undefined, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current).toEqual({query: null, data: null}) }) it('returns nulls when intentQuery is missing', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: {other: 'param'}, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current).toEqual({query: null, data: null}) }) }) describe('intent query parsing', () => { it('parses action:type,value format', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'try_on:shopify/Product,gid://shopify/Product/123', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.query).toEqual({ action: 'try_on', type: 'shopify/Product', value: 'gid://shopify/Product/123', }) expect(result.current.data).toBeNull() }) it('parses action:type without value', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: {intentQuery: 'create:shopify/Product'}, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.query).toEqual({ action: 'create', type: 'shopify/Product', value: null, }) }) it('handles URL-encoded intentQuery', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'try_on%3Ashopify%2FProduct%2Cgid%3A%2F%2Fshopify%2FProduct%2F123', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.query).toEqual({ action: 'try_on', type: 'shopify/Product', value: 'gid://shopify/Product/123', }) }) it('returns null query for invalid format without colon', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: {intentQuery: 'invalid-no-colon'}, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current).toEqual({query: null, data: null}) }) it('returns null query when action is empty', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: ':shopify/Product,gid://shopify/Product/123', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current).toEqual({query: null, data: null}) }) it('returns null value for trailing comma', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: {intentQuery: 'create:shopify/Product,'}, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.query).toEqual({ action: 'create', type: 'shopify/Product', value: null, }) }) it('returns null query when type is empty', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: {intentQuery: 'try_on:'}, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current).toEqual({query: null, data: null}) }) }) describe('shorthand GID syntax', () => { it('infers type and value from full GID', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'edit:gid://shopify/Product/123', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.query).toEqual({ action: 'edit', type: 'shopify/Product', value: 'gid://shopify/Product/123', }) }) it('infers type with null value from GID without ID', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'create:gid://shopify/Product', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.query).toEqual({ action: 'create', type: 'shopify/Product', value: null, }) }) it('parses intentData alongside shorthand GID', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'edit:gid://shopify/Product/123', intentData: '{"title":"Updated"}', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.query).toEqual({ action: 'edit', type: 'shopify/Product', value: 'gid://shopify/Product/123', }) expect(result.current.data).toEqual({title: 'Updated'}) }) }) describe('intent data parsing', () => { it('parses JSON intentData', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'try_on:shopify/Product,gid://shopify/Product/123', intentData: '{"variantId":"gid://shopify/ProductVariant/456"}', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.data).toEqual({ variantId: 'gid://shopify/ProductVariant/456', }) }) it('parses URL-encoded intentData', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'try_on:shopify/Product,gid://shopify/Product/123', intentData: '%7B%22variantId%22%3A%22456%22%7D', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.data).toEqual({variantId: '456'}) }) it('preserves percent-encoded values inside already-decoded JSON', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'edit:shopify/Product,gid://shopify/Product/123', intentData: '{"redirect":"https%3A%2F%2Fexample.com"}', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.data).toEqual({ redirect: 'https%3A%2F%2Fexample.com', }) }) it('returns null data when intentData is a JSON array', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'try_on:shopify/Product,gid://shopify/Product/123', intentData: '[1,2,3]', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.query).not.toBeNull() expect(result.current.data).toBeNull() }) it('returns null data when intentData is a JSON primitive', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'try_on:shopify/Product,gid://shopify/Product/123', intentData: '"just a string"', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.query).not.toBeNull() expect(result.current.data).toBeNull() }) it('returns null data for malformed JSON', () => { vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams: { intentQuery: 'try_on:shopify/Product,gid://shopify/Product/123', intentData: 'not-valid-json', }, hash: '', }) const {result} = renderHook(() => useIntent()) expect(result.current.query).not.toBeNull() expect(result.current.data).toBeNull() }) }) describe('memoization', () => { it('returns same reference when queryParams do not change', () => { const queryParams = { intentQuery: 'try_on:shopify/Product,gid://shopify/Product/123', } vi.mocked(useDeeplink).mockReturnValue({ path: '/', queryParams, hash: '', }) const {result, rerender} = renderHook(() => useIntent()) const firstResult = result.current rerender() expect(result.current).toBe(firstResult) }) }) })