Skip to content

Commit 3f34454

Browse files
W-18981858 add tests and fix image/desc spacing issue (#3049)
* W-18981858 add tests and fix image/desc spacing issue * W-18981858 fix linting * W-18981858 chakra for widths * W-18981858 chakra for widths
1 parent 5d77b5a commit 3f34454

File tree

3 files changed

+279
-13
lines changed

3 files changed

+279
-13
lines changed

packages/template-retail-react-app/app/components/search/partials/horizontal-suggestions.jsx

Lines changed: 30 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
*/
77
import React from 'react'
88
import PropTypes from 'prop-types'
9-
import {Text, Box, Flex} from '@salesforce/retail-react-app/app/components/shared/ui'
9+
import {Text, Box, Flex, AspectRatio} from '@salesforce/retail-react-app/app/components/shared/ui'
1010
import DynamicImage from '@salesforce/retail-react-app/app/components/dynamic-image'
1111
import Link from '@salesforce/retail-react-app/app/components/link'
1212

@@ -25,19 +25,36 @@ const HorizontalSuggestions = ({suggestions, closeAndNavigate, dynamicImageProps
2525
key={idx}
2626
onClick={() => closeAndNavigate(suggestion.link)}
2727
>
28-
<Box>
28+
<Box width={{base: '50vw', md: '50vw', lg: '10vw'}} flex="0 0 auto">
2929
{/* Product Image */}
30-
<Box position="relative" mb="2" minH="200px">
31-
{suggestion.image && (
32-
<DynamicImage
33-
src={`${suggestion.image}[?sw={width}&q=60]`}
34-
widths={dynamicImageProps?.widths}
35-
imageProps={{
36-
alt: '',
37-
loading: 'eager'
38-
}}
39-
/>
40-
)}
30+
<Box mb="2">
31+
{suggestion.image ? (
32+
<AspectRatio ratio={1}>
33+
<DynamicImage
34+
src={`${suggestion.image}[?sw={width}&q=60]`}
35+
widths={dynamicImageProps?.widths}
36+
sx={{
37+
height: '100%',
38+
width: '100%',
39+
'& picture': {
40+
display: 'block',
41+
height: '100%',
42+
width: '100%'
43+
},
44+
'& img': {
45+
display: 'block',
46+
height: '100%',
47+
width: '100%',
48+
objectFit: 'cover'
49+
}
50+
}}
51+
imageProps={{
52+
alt: '',
53+
loading: 'eager'
54+
}}
55+
/>
56+
</AspectRatio>
57+
) : null}
4158
</Box>
4259

4360
<Text
Lines changed: 110 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,110 @@
1+
/*
2+
* Copyright (c) 2021, salesforce.com, inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
import React from 'react'
8+
import {screen} from '@testing-library/react'
9+
import userEvent from '@testing-library/user-event'
10+
import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
11+
import HorizontalSuggestions from '@salesforce/retail-react-app/app/components/search/partials/horizontal-suggestions'
12+
13+
jest.mock('@salesforce/retail-react-app/app/components/dynamic-image', () => {
14+
return function MockDynamicImage(props) {
15+
const {src, widths, imageProps} = props || {}
16+
return (
17+
<img
18+
data-testid="dynamic-image"
19+
data-src={src}
20+
data-widths={(widths || []).join(',')}
21+
alt={imageProps?.alt ?? ''}
22+
/>
23+
)
24+
}
25+
})
26+
27+
const sampleSuggestions = [
28+
{
29+
link: '/product-1',
30+
image: 'https://example.com/image-1.jpg',
31+
name: 'Product 1',
32+
price: '29.99'
33+
},
34+
{
35+
link: '/product-2',
36+
name: 'Product 2'
37+
}
38+
]
39+
40+
test('returns null when suggestions are undefined', () => {
41+
renderWithProviders(
42+
<HorizontalSuggestions suggestions={undefined} closeAndNavigate={jest.fn()} />
43+
)
44+
expect(screen.queryByTestId('sf-horizontal-product-suggestions')).not.toBeInTheDocument()
45+
})
46+
47+
test('renders product tiles with names and optional prices', () => {
48+
renderWithProviders(
49+
<HorizontalSuggestions suggestions={sampleSuggestions} closeAndNavigate={jest.fn()} />
50+
)
51+
52+
// container
53+
expect(screen.getByTestId('sf-horizontal-product-suggestions')).toBeInTheDocument()
54+
55+
// tiles
56+
const tiles = screen.getAllByTestId('product-tile')
57+
expect(tiles).toHaveLength(2)
58+
59+
// names
60+
expect(screen.getByText('Product 1')).toBeInTheDocument()
61+
expect(screen.getByText('Product 2')).toBeInTheDocument()
62+
63+
// price only for first suggestion
64+
expect(screen.getByText('$29.99')).toBeInTheDocument()
65+
})
66+
67+
test('renders DynamicImage when image is provided and passes widths via dynamicImageProps', () => {
68+
const dynamicImageProps = {widths: [200, 400, 800]}
69+
renderWithProviders(
70+
<HorizontalSuggestions
71+
suggestions={sampleSuggestions}
72+
closeAndNavigate={jest.fn()}
73+
dynamicImageProps={dynamicImageProps}
74+
/>
75+
)
76+
77+
const images = screen.getAllByTestId('dynamic-image')
78+
// Only first suggestion has an image
79+
expect(images).toHaveLength(1)
80+
81+
const img = images[0]
82+
// src should be appended with the width token by the component
83+
expect(img.getAttribute('data-src')).toBe(`${sampleSuggestions[0].image}[?sw={width}&q=60]`)
84+
expect(img.getAttribute('data-widths')).toBe('200,400,800')
85+
})
86+
87+
test('does not render DynamicImage when image is absent', () => {
88+
renderWithProviders(
89+
<HorizontalSuggestions suggestions={[sampleSuggestions[1]]} closeAndNavigate={jest.fn()} />
90+
)
91+
92+
expect(screen.queryByTestId('dynamic-image')).not.toBeInTheDocument()
93+
})
94+
95+
test('clicking a product tile triggers closeAndNavigate with the link', async () => {
96+
const user = userEvent.setup()
97+
const closeAndNavigate = jest.fn()
98+
99+
renderWithProviders(
100+
<HorizontalSuggestions
101+
suggestions={sampleSuggestions}
102+
closeAndNavigate={closeAndNavigate}
103+
/>
104+
)
105+
106+
const tiles = screen.getAllByTestId('product-tile')
107+
await user.click(tiles[1])
108+
109+
expect(closeAndNavigate).toHaveBeenCalledWith(sampleSuggestions[1].link)
110+
})
Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
/*
2+
* Copyright (c) 2021, salesforce.com, inc.
3+
* All rights reserved.
4+
* SPDX-License-Identifier: BSD-3-Clause
5+
* For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6+
*/
7+
import React from 'react'
8+
import {screen, within} from '@testing-library/react'
9+
import userEvent from '@testing-library/user-event'
10+
import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
11+
import SuggestionSection from '@salesforce/retail-react-app/app/components/search/partials/search-suggestions-section'
12+
13+
// Mock dynamic image to keep DOM simple when HorizontalSuggestions renders
14+
jest.mock('@salesforce/retail-react-app/app/components/dynamic-image', () => {
15+
return function MockDynamicImage(props) {
16+
const {src, widths, imageProps} = props || {}
17+
return (
18+
<img
19+
data-testid="dynamic-image"
20+
data-src={src}
21+
data-widths={(widths || []).join(',')}
22+
alt={imageProps?.alt ?? ''}
23+
/>
24+
)
25+
}
26+
})
27+
28+
const baseStyles = {
29+
textContainer: {},
30+
sectionHeader: {},
31+
phraseContainer: {}
32+
}
33+
34+
const makeSearchSuggestions = (overrides = {}) => ({
35+
searchPhrase: 'Dress',
36+
phraseSuggestions: [],
37+
categorySuggestions: [],
38+
productSuggestions: [],
39+
...overrides
40+
})
41+
42+
test('renders "Did you mean" with suggestion link when non-exact phrase exists (mobile and desktop)', () => {
43+
const searchSuggestions = makeSearchSuggestions({
44+
phraseSuggestions: [{name: 'dresses', link: '/search?q=dresses', exactMatch: false}]
45+
})
46+
47+
renderWithProviders(
48+
<SuggestionSection
49+
searchSuggestions={searchSuggestions}
50+
closeAndNavigate={jest.fn()}
51+
styles={baseStyles}
52+
/>
53+
)
54+
55+
// Appears in both mobile and desktop sections
56+
const didYouMeanTexts = screen.getAllByText(/Did you mean/i)
57+
expect(didYouMeanTexts.length).toBeGreaterThanOrEqual(1)
58+
59+
const links = screen.getAllByRole('link', {name: /dresses\?/i})
60+
expect(links.length).toBeGreaterThanOrEqual(1)
61+
})
62+
63+
test('renders Categories header and category suggestions', () => {
64+
const searchSuggestions = makeSearchSuggestions({
65+
categorySuggestions: [
66+
{type: 'category', name: 'Women', link: '/women'},
67+
{type: 'category', name: 'Men', link: '/men'}
68+
]
69+
})
70+
71+
renderWithProviders(
72+
<SuggestionSection
73+
searchSuggestions={searchSuggestions}
74+
closeAndNavigate={jest.fn()}
75+
styles={baseStyles}
76+
/>
77+
)
78+
79+
// Header present (could be duplicated for mobile/desktop)
80+
expect(screen.getAllByText('Categories').length).toBeGreaterThanOrEqual(1)
81+
82+
// Suggestions component renders buttons; ensure names are present
83+
expect(screen.getAllByText('Women').length).toBeGreaterThanOrEqual(1)
84+
expect(screen.getAllByText('Men').length).toBeGreaterThanOrEqual(1)
85+
})
86+
87+
test('renders horizontal product suggestions and "View All"; clicking a tile calls closeAndNavigate', async () => {
88+
const user = userEvent.setup()
89+
const closeAndNavigate = jest.fn()
90+
91+
const searchSuggestions = makeSearchSuggestions({
92+
productSuggestions: [
93+
{
94+
type: 'product',
95+
name: 'Product 1',
96+
link: '/p1',
97+
image: 'https://example.com/p1.jpg',
98+
price: '19.99'
99+
},
100+
{type: 'product', name: 'Product 2', link: '/p2'}
101+
]
102+
})
103+
104+
renderWithProviders(
105+
<SuggestionSection
106+
searchSuggestions={searchSuggestions}
107+
closeAndNavigate={closeAndNavigate}
108+
styles={baseStyles}
109+
/>
110+
)
111+
112+
// HorizontalSuggestions container
113+
expect(screen.getByTestId('sf-horizontal-product-suggestions')).toBeInTheDocument()
114+
115+
// "View All" link only renders when products exist (may be hidden by responsive wrapper in tests)
116+
expect(screen.getByText(/View All/i, {selector: 'a'})).toBeInTheDocument()
117+
118+
// Click a product tile (desktop horizontal suggestions)
119+
const container = screen.getByTestId('sf-horizontal-product-suggestions')
120+
const tiles = within(container).getAllByTestId('product-tile')
121+
await user.click(tiles[1])
122+
expect(closeAndNavigate).toHaveBeenCalledWith('/p2')
123+
})
124+
125+
test('renders nothing when there are no categories, products, or phrase suggestions', () => {
126+
const searchSuggestions = makeSearchSuggestions()
127+
128+
renderWithProviders(
129+
<SuggestionSection
130+
searchSuggestions={searchSuggestions}
131+
closeAndNavigate={jest.fn()}
132+
styles={baseStyles}
133+
/>
134+
)
135+
136+
expect(screen.queryByText('Categories')).not.toBeInTheDocument()
137+
expect(screen.queryByText('Products')).not.toBeInTheDocument()
138+
expect(screen.queryByTestId('sf-horizontal-product-suggestions')).not.toBeInTheDocument()
139+
})

0 commit comments

Comments
 (0)