Files
flyer-crawler.projectium.com/src/features/store/StoreCard.test.tsx
Torben Sorensen 45ac4fccf5
Some checks failed
Deploy to Test Environment / deploy-to-test (push) Failing after 2m15s
comprehensive documentation review + test fixes
2026-01-28 16:35:38 -08:00

393 lines
12 KiB
TypeScript

// src/features/store/StoreCard.test.tsx
import React from 'react';
import { screen } from '@testing-library/react';
import { describe, it, expect, beforeEach, vi } from 'vitest';
import { StoreCard } from './StoreCard';
import { renderWithProviders } from '../../tests/utils/renderWithProviders';
describe('StoreCard', () => {
const mockStoreWithLogo = {
store_id: 1,
name: 'Test Store',
logo_url: 'https://example.com/logo.png',
locations: [
{
address_line_1: '123 Main Street',
city: 'Toronto',
province_state: 'ON',
postal_code: 'M5V 1A1',
},
],
};
const mockStoreWithoutLogo = {
store_id: 2,
name: 'Another Store',
logo_url: null,
locations: [
{
address_line_1: '456 Oak Avenue',
city: 'Vancouver',
province_state: 'BC',
postal_code: 'V6B 2M9',
},
],
};
const mockStoreWithMultipleLocations = {
store_id: 3,
name: 'Multi Location Store',
logo_url: 'https://example.com/multi-logo.png',
locations: [
{
address_line_1: '100 First Street',
city: 'Montreal',
province_state: 'QC',
postal_code: 'H2X 1Y6',
},
{
address_line_1: '200 Second Street',
city: 'Montreal',
province_state: 'QC',
postal_code: 'H3A 2T1',
},
{
address_line_1: '300 Third Street',
city: 'Montreal',
province_state: 'QC',
postal_code: 'H4B 3C2',
},
],
};
const mockStoreNoLocations = {
store_id: 4,
name: 'No Location Store',
logo_url: 'https://example.com/no-loc-logo.png',
locations: [],
};
const mockStoreUndefinedLocations = {
store_id: 5,
name: 'Undefined Locations Store',
logo_url: null,
};
beforeEach(() => {
vi.clearAllMocks();
});
describe('store name rendering', () => {
it('should render the store name', () => {
renderWithProviders(<StoreCard store={mockStoreWithLogo} />);
expect(screen.getByText('Test Store')).toBeInTheDocument();
});
it('should render store name with truncation class', () => {
renderWithProviders(<StoreCard store={mockStoreWithLogo} />);
const heading = screen.getByRole('heading', { level: 3 });
expect(heading).toHaveClass('truncate');
});
});
describe('logo rendering', () => {
it('should render logo image when logo_url is provided', () => {
renderWithProviders(<StoreCard store={mockStoreWithLogo} />);
const logo = screen.getByAltText('Test Store logo');
expect(logo).toBeInTheDocument();
expect(logo).toHaveAttribute('src', 'https://example.com/logo.png');
});
it('should render initials fallback when logo_url is null', () => {
renderWithProviders(<StoreCard store={mockStoreWithoutLogo} />);
expect(screen.getByText('AN')).toBeInTheDocument();
});
it('should render initials fallback when logo_url is undefined', () => {
const storeWithUndefinedLogo = {
store_id: 10,
name: 'Test Name',
logo_url: undefined,
};
renderWithProviders(<StoreCard store={storeWithUndefinedLogo} />);
expect(screen.getByText('TE')).toBeInTheDocument();
});
it('should convert initials to uppercase', () => {
const storeWithLowercase = {
store_id: 11,
name: 'lowercase store',
logo_url: null,
};
renderWithProviders(<StoreCard store={storeWithLowercase} />);
expect(screen.getByText('LO')).toBeInTheDocument();
});
it('should handle single character store name', () => {
const singleCharStore = {
store_id: 12,
name: 'X',
logo_url: null,
};
renderWithProviders(<StoreCard store={singleCharStore} />);
// Both the store name and initials will be 'X'
// Check that there are exactly 2 elements with 'X'
const elements = screen.getAllByText('X');
expect(elements).toHaveLength(2);
});
it('should handle empty string store name', () => {
const emptyNameStore = {
store_id: 13,
name: '',
logo_url: null,
};
// This will render empty string for initials
const { container } = renderWithProviders(<StoreCard store={emptyNameStore} />);
// The fallback div should still render
const fallbackDiv = container.querySelector('.h-12.w-12.flex');
expect(fallbackDiv).toBeInTheDocument();
});
});
describe('location display', () => {
it('should not show location when showLocations is false (default)', () => {
renderWithProviders(<StoreCard store={mockStoreWithLogo} />);
expect(screen.queryByText('123 Main Street')).not.toBeInTheDocument();
expect(screen.queryByText(/Toronto/)).not.toBeInTheDocument();
});
it('should show primary location when showLocations is true', () => {
renderWithProviders(<StoreCard store={mockStoreWithLogo} showLocations={true} />);
expect(screen.getByText('123 Main Street')).toBeInTheDocument();
expect(screen.getByText('Toronto, ON M5V 1A1')).toBeInTheDocument();
});
it('should show "No location data" when showLocations is true but no locations exist', () => {
renderWithProviders(<StoreCard store={mockStoreNoLocations} showLocations={true} />);
expect(screen.getByText('No location data')).toBeInTheDocument();
});
it('should show "No location data" when locations is undefined', () => {
renderWithProviders(
<StoreCard store={mockStoreUndefinedLocations as any} showLocations={true} />,
);
expect(screen.getByText('No location data')).toBeInTheDocument();
});
it('should not show "No location data" message when showLocations is false', () => {
renderWithProviders(<StoreCard store={mockStoreNoLocations} showLocations={false} />);
expect(screen.queryByText('No location data')).not.toBeInTheDocument();
});
});
describe('multiple locations', () => {
it('should show additional locations count for 2 locations', () => {
const storeWith2Locations = {
...mockStoreWithLogo,
locations: [
mockStoreWithMultipleLocations.locations[0],
mockStoreWithMultipleLocations.locations[1],
],
};
renderWithProviders(<StoreCard store={storeWith2Locations} showLocations={true} />);
expect(screen.getByText('+ 1 more location')).toBeInTheDocument();
});
it('should show additional locations count for 3+ locations', () => {
renderWithProviders(
<StoreCard store={mockStoreWithMultipleLocations} showLocations={true} />,
);
expect(screen.getByText('+ 2 more locations')).toBeInTheDocument();
});
it('should show primary location from multiple locations', () => {
renderWithProviders(
<StoreCard store={mockStoreWithMultipleLocations} showLocations={true} />,
);
// Should show first location
expect(screen.getByText('100 First Street')).toBeInTheDocument();
expect(screen.getByText('Montreal, QC H2X 1Y6')).toBeInTheDocument();
// Should NOT show secondary locations directly
expect(screen.queryByText('200 Second Street')).not.toBeInTheDocument();
});
it('should not show additional locations count for single location', () => {
renderWithProviders(<StoreCard store={mockStoreWithLogo} showLocations={true} />);
expect(screen.queryByText(/more location/)).not.toBeInTheDocument();
});
});
describe('accessibility', () => {
it('should have proper alt text for logo', () => {
renderWithProviders(<StoreCard store={mockStoreWithLogo} />);
const logo = screen.getByAltText('Test Store logo');
expect(logo).toBeInTheDocument();
});
it('should use heading level 3 for store name', () => {
renderWithProviders(<StoreCard store={mockStoreWithLogo} />);
const heading = screen.getByRole('heading', { level: 3 });
expect(heading).toHaveTextContent('Test Store');
});
});
describe('styling', () => {
it('should apply flex layout to container', () => {
const { container } = renderWithProviders(<StoreCard store={mockStoreWithLogo} />);
const mainDiv = container.firstChild;
expect(mainDiv).toHaveClass('flex', 'items-start', 'space-x-3');
});
it('should apply proper styling to logo image', () => {
renderWithProviders(<StoreCard store={mockStoreWithLogo} />);
const logo = screen.getByAltText('Test Store logo');
expect(logo).toHaveClass(
'h-12',
'w-12',
'object-contain',
'rounded-md',
'bg-gray-100',
'dark:bg-gray-700',
'p-1',
'flex-shrink-0',
);
});
it('should apply proper styling to initials fallback', () => {
const { container } = renderWithProviders(<StoreCard store={mockStoreWithoutLogo} />);
const initialsDiv = container.querySelector('.h-12.w-12.flex.items-center.justify-center');
expect(initialsDiv).toHaveClass(
'h-12',
'w-12',
'flex',
'items-center',
'justify-center',
'bg-gray-200',
'dark:bg-gray-700',
'rounded-md',
'text-gray-400',
'text-xs',
'flex-shrink-0',
);
});
it('should apply italic style to "No location data" text', () => {
renderWithProviders(<StoreCard store={mockStoreNoLocations} showLocations={true} />);
const noLocationText = screen.getByText('No location data');
expect(noLocationText).toHaveClass('italic');
});
});
describe('edge cases', () => {
it('should handle store with special characters in name', () => {
const specialCharStore = {
store_id: 20,
name: "Store & Co's <Best>",
logo_url: null,
};
renderWithProviders(<StoreCard store={specialCharStore} />);
expect(screen.getByText("Store & Co's <Best>")).toBeInTheDocument();
expect(screen.getByText('ST')).toBeInTheDocument();
});
it('should handle store with unicode characters', () => {
const unicodeStore = {
store_id: 21,
name: 'Cafe Le Cafe',
logo_url: null,
};
renderWithProviders(<StoreCard store={unicodeStore} />);
expect(screen.getByText('Cafe Le Cafe')).toBeInTheDocument();
expect(screen.getByText('CA')).toBeInTheDocument();
});
it('should handle location with long address', () => {
const longAddressStore = {
store_id: 22,
name: 'Long Address Store',
logo_url: 'https://example.com/logo.png',
locations: [
{
address_line_1: '1234567890 Very Long Street Name That Exceeds Normal Length',
city: 'Vancouver',
province_state: 'BC',
postal_code: 'V6B 2M9',
},
],
};
renderWithProviders(<StoreCard store={longAddressStore} showLocations={true} />);
const addressElement = screen.getByText(
'1234567890 Very Long Street Name That Exceeds Normal Length',
);
expect(addressElement).toHaveClass('truncate');
});
});
describe('data types', () => {
it('should accept store_id as number', () => {
const store = {
store_id: 12345,
name: 'Numeric ID Store',
logo_url: null,
};
// This should compile and render without errors
renderWithProviders(<StoreCard store={store} />);
expect(screen.getByText('Numeric ID Store')).toBeInTheDocument();
});
it('should handle empty logo_url string', () => {
const storeWithEmptyLogo = {
store_id: 30,
name: 'Empty Logo Store',
logo_url: '',
};
// Empty string is truthy check, but might cause issues with img src
// The component checks for truthy logo_url, so empty string will render initials
// Actually, empty string '' is falsy in JavaScript, so this would show initials
renderWithProviders(<StoreCard store={storeWithEmptyLogo} />);
// Empty string is falsy, so initials should show
expect(screen.getByText('EM')).toBeInTheDocument();
});
});
});