import {describe, expect, it, vi, beforeEach, afterEach} from 'vitest'
import {
render,
screen,
userEvent,
mockShop,
mockMinisSDK,
resetAllMocks,
} from '../../test-utils'
import {
MerchantCard,
MerchantCardContainer,
MerchantCardHeader,
MerchantCardInfo,
MerchantCardName,
MerchantCardRating,
} from './merchant-card'
// Mock hooks
vi.mock('../../hooks/navigation/useShopNavigation', () => ({
useShopNavigation: () => ({
navigateToShop: mockMinisSDK.navigateToShop,
navigateToProduct: mockMinisSDK.navigateToProduct,
}),
}))
// Mock utils with simple implementations
vi.mock('../../utils', () => ({
extractBrandTheme: vi.fn(() => ({
type: 'default',
backgroundColor: 'white',
})),
getFeaturedImages: vi.fn(() => []),
formatReviewCount: vi.fn((count: number) => {
if (count >= 1000) return `${(count / 1000).toFixed(1)}k`
return count.toString()
}),
normalizeRating: vi.fn((rating: number) => rating.toFixed(1)),
}))
vi.mock('../../utils/colors', () => ({
isDarkColor: vi.fn(() => false),
}))
describe('MerchantCard', () => {
beforeEach(() => {
resetAllMocks()
})
afterEach(() => {
vi.clearAllMocks()
})
describe('Rendering', () => {
it('renders with basic shop information', () => {
const shop = mockShop()
render()
expect(screen.getByText(shop.name)).toBeInTheDocument()
// createShop returns 4.3 rating with 50 reviews
expect(screen.getByText('4.3 (50)')).toBeInTheDocument() // Rating and review count
})
it('renders shop logo when available', () => {
const shop = mockShop({
visualTheme: {
logoImage: {
url: 'https://example.com/logo.png',
altText: 'Shop Logo',
sensitive: false,
},
featuredImages: [],
id: '123',
},
})
render()
const logo = screen.getByAltText(`${shop.name} logo`)
expect(logo).toBeInTheDocument()
expect(logo).toHaveAttribute('src', 'https://example.com/logo.png')
})
it('renders shop initial when logo is not available', () => {
const shop = mockShop({
name: 'Test Shop',
visualTheme: null,
})
render()
expect(screen.getByText('T')).toBeInTheDocument()
})
it('renders without reviews when not available', () => {
const shop = mockShop({
reviewAnalytics: {
averageRating: null,
reviewCount: 0,
},
})
render()
expect(
screen.queryByTestId('merchant-card-rating')
).not.toBeInTheDocument()
})
})
describe('Interactions', () => {
it('handles shop click navigation', async () => {
const user = userEvent.setup()
const shop = mockShop()
render()
const card = screen.getByText(shop.name).closest('div')
?.parentElement?.parentElement
await user.click(card!)
expect(mockMinisSDK.navigateToShop).toHaveBeenCalledWith({
shopId: shop.id,
})
})
it('does not navigate when touchable is false', async () => {
const user = userEvent.setup()
const shop = mockShop()
render()
const card = screen.getByText(shop.name).closest('div')
?.parentElement?.parentElement
await user.click(card!)
expect(mockMinisSDK.navigateToShop).not.toHaveBeenCalled()
})
})
describe('Custom Composition', () => {
it('renders with custom children layout', () => {
const shop = mockShop()
render(
Custom Name
)
expect(screen.getByTestId('custom-layout')).toBeInTheDocument()
expect(screen.getByText('Custom Name')).toBeInTheDocument()
// createShop returns 4.3 rating with 50 reviews
expect(screen.getByText('4.3 (50)')).toBeInTheDocument()
})
it('allows individual component usage', () => {
const shop = mockShop()
render(
)
expect(screen.getByText(shop.name)).toBeInTheDocument()
// createShop returns 4.3 rating with 50 reviews
expect(screen.getByText('4.3 (50)')).toBeInTheDocument()
})
})
describe('Edge Cases', () => {
it('handles shop without visual theme', () => {
const shop = mockShop({
visualTheme: null,
})
render()
expect(screen.getByText(shop.name)).toBeInTheDocument()
})
it('handles shop with empty name', () => {
const shop = mockShop({
name: '',
})
render()
// Should still render with empty name
const heading = screen.getByRole('heading', {level: 3})
expect(heading).toBeInTheDocument()
})
it('handles very long shop names', () => {
const shop = mockShop({
name: 'This is a very long shop name that should be truncated in the UI',
})
render()
const nameElement = screen.getByText(shop.name)
expect(nameElement).toHaveClass('truncate')
})
it('handles high review counts', () => {
const shop = mockShop({
reviewAnalytics: {
averageRating: 4.8,
reviewCount: 12500,
},
})
render()
expect(screen.getByText('4.8 (12.5k)')).toBeInTheDocument()
})
})
describe('Accessibility', () => {
it('maintains proper heading hierarchy', () => {
const shop = mockShop()
render()
const heading = screen.getByRole('heading', {level: 3})
expect(heading).toHaveTextContent(shop.name)
})
it('provides fallback alt text for logo', () => {
const shop = mockShop({
visualTheme: {
logoImage: {
url: 'https://example.com/logo.png',
altText: null,
sensitive: false,
},
featuredImages: [],
id: '123',
},
})
render()
const logo = screen.getByAltText(`${shop.name} logo`)
expect(logo).toBeInTheDocument()
})
})
})