@salesforce/retail-react-app 6.1.0 → 7.0.0-nightly-20250526080400

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -1,3 +1,7 @@
1
+ ## v7.0.0-dev.0 (May 20, 2025)
2
+
3
+ - Improved the layout of product tiles in product scroll and product list [#2446](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2446)
4
+
1
5
  ## v6.1.0 (May 22, 2025)
2
6
 
3
7
  - Fix hreflang alternate links [#2269](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2269)
@@ -101,6 +105,7 @@
101
105
  - Add aria-label for Checkout's action buttons [#1906](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/1906)
102
106
  - Avoid forced focus changes that are not user-initiated [#1940](https://github.com/SalesforceCommerceCloud/pwa-kit/pull/1940)
103
107
 
108
+
104
109
  ## v3.0.2 (Jul 11, 2024)
105
110
 
106
111
  ### Bug Fixes
@@ -141,14 +141,14 @@ NestedAccordion.propTypes = {
141
141
  item: PropTypes.object.isRequired,
142
142
  /**
143
143
  * An array of `AccordionItem` components that will be displayed after all
144
- * of the child items. Alternatively you can pass a function that will recieve
144
+ * of the child items. Alternatively you can pass a function that will receive
145
145
  * the current item and it's depth, your should return an `AccordionItem` or
146
146
  * array of `AccordionItem`'s.
147
147
  */
148
148
  itemsAfter: PropTypes.oneOfType([PropTypes.array, PropTypes.func]),
149
149
  /**
150
150
  * An array of `AccordionItem` components that will be displayed before all
151
- * of the child items. Alternatively you can pass a function that will recieve
151
+ * of the child items. Alternatively you can pass a function that will receive
152
152
  * the current item and it's depth, your should return an `AccordionItem` or
153
153
  * array of `AccordionItem`'s.
154
154
  */
@@ -30,7 +30,7 @@ const ProductScroller = forwardRef(
30
30
  products,
31
31
  isLoading,
32
32
  scrollProps,
33
- itemWidth = {base: '70%', md: '40%', lg: 'calc(33.33% - 10px)'},
33
+ itemWidth = {base: '70%', md: '40%', lg: 'calc(25% - 15px)'},
34
34
  productTileProps,
35
35
  ...props
36
36
  },
@@ -86,7 +86,7 @@ test('Renders variant details based on the selected swatch', async () => {
86
86
  // Initial render will show swatched and the image will be the represented product variation
87
87
  expect(swatches).toHaveLength(2)
88
88
  expect(productImage.firstChild.getAttribute('src')).toBe(
89
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw175c1a89/images/large/PG.33698RUBN4Q.CHARCWL.PZ.jpg'
89
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw23283a20/images/medium/PG.33698RUBN4Q.CHARCWL.PZ.jpg'
90
90
  )
91
91
  const currentPriceTag = productTile.querySelectorAll('b')
92
92
  const strikethroughPriceTag = productTile.querySelectorAll('s')
@@ -100,7 +100,7 @@ test('Renders variant details based on the selected swatch', async () => {
100
100
  fireEvent.mouseOver(swatches[0])
101
101
  await waitFor(() => screen.getByTestId('product-tile-image'))
102
102
  expect(productImage.firstChild.getAttribute('src')).toBe(
103
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw29b7f226/images/large/PG.52002RUBN4Q.NAVYWL.PZ.jpg'
103
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed78c6fc/images/medium/PG.52002RUBN4Q.NAVYWL.PZ.jpg'
104
104
  )
105
105
  expect(currentPriceTag).toHaveLength(1)
106
106
  expect(within(currentPriceTag[0]).getByText(/£143\.99/i)).toBeDefined()
@@ -122,7 +122,7 @@ test('Renders variant details based on the selected swatch on mobile', async ()
122
122
  // Initial render will show swatched and the image will be the represented product variation
123
123
  expect(swatches).toHaveLength(2)
124
124
  expect(productImage.firstChild.getAttribute('src')).toBe(
125
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw175c1a89/images/large/PG.33698RUBN4Q.CHARCWL.PZ.jpg'
125
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw23283a20/images/medium/PG.33698RUBN4Q.CHARCWL.PZ.jpg'
126
126
  )
127
127
  const currentPriceTag = productTile.querySelectorAll('b')
128
128
  const strikethroughPriceTag = productTile.querySelectorAll('s')
@@ -136,7 +136,7 @@ test('Renders variant details based on the selected swatch on mobile', async ()
136
136
  fireEvent.click(swatches[0])
137
137
  await waitFor(() => screen.getByTestId('product-tile-image'))
138
138
  expect(productImage.firstChild.getAttribute('src')).toBe(
139
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw29b7f226/images/large/PG.52002RUBN4Q.NAVYWL.PZ.jpg'
139
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed78c6fc/images/medium/PG.52002RUBN4Q.NAVYWL.PZ.jpg'
140
140
  )
141
141
  expect(currentPriceTag).toHaveLength(1)
142
142
  expect(within(currentPriceTag[0]).getByText(/£143\.99/i)).toBeDefined()
@@ -154,7 +154,7 @@ test('Renders price range with starting price and strikethrough price for master
154
154
  expect(getByText(/Long Sleeve Embellished Boat Neck Top/i)).toBeInTheDocument()
155
155
  const productImage = getByTestId('product-tile-image')
156
156
  expect(productImage.firstChild.getAttribute('src')).toBe(
157
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3255ea4c/images/large/PG.10217069.JJ908XX.PZ.jpg'
157
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc9ce7da9/images/medium/PG.10217069.JJ908XX.PZ.jpg'
158
158
  )
159
159
 
160
160
  const currentPriceTag = container.querySelectorAll('b')
@@ -171,7 +171,7 @@ test('Renders price range with starting price and strikethrough price for master
171
171
  fireEvent.mouseOver(swatches[0])
172
172
  await waitFor(() => screen.getByTestId('product-tile-image'))
173
173
  expect(productImage.firstChild.getAttribute('src')).toBe(
174
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e4c00a0/images/large/PG.10217069.JJ5QZXX.PZ.jpg'
174
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed56b2da/images/medium/PG.10217069.JJ5QZXX.PZ.jpg'
175
175
  )
176
176
  expect(currentPriceTag).toHaveLength(1)
177
177
  expect(within(currentPriceTag[0]).getByText(/From £18\.55/i)).toBeDefined()
@@ -128,6 +128,29 @@ test('Product View can update quantity', async () => {
128
128
  })
129
129
  })
130
130
 
131
+ describe('ProductView Component', () => {
132
+ test('increases quantity when increment button is clicked', async () => {
133
+ const user = userEvent.setup()
134
+ renderWithProviders(<ProductView product={mockProductDetail} />)
135
+
136
+ const quantityInput = await screen.findByRole('spinbutton')
137
+ const incrementButton = screen.getByTestId('quantity-increment')
138
+ const decrementButton = screen.getByTestId('quantity-decrement')
139
+
140
+ // Click increment
141
+ await user.click(incrementButton)
142
+ await waitFor(() => {
143
+ expect(quantityInput).toHaveValue('2')
144
+ })
145
+
146
+ // Click decrement
147
+ await user.click(decrementButton)
148
+ await waitFor(() => {
149
+ expect(quantityInput).toHaveValue('1')
150
+ })
151
+ })
152
+ })
153
+
131
154
  test('renders a product set properly - parent item', () => {
132
155
  const parent = mockProductSet
133
156
  renderWithProviders(
@@ -5,7 +5,7 @@
5
5
  * For full license text, see the LICENSE file in the repo root or https://opensource.org/licenses/BSD-3-Clause
6
6
  */
7
7
  import React, {useState} from 'react'
8
- import {screen} from '@testing-library/react'
8
+ import {screen, fireEvent} from '@testing-library/react'
9
9
  import userEvent from '@testing-library/user-event'
10
10
  import {renderWithProviders} from '@salesforce/retail-react-app/app/utils/test-utils'
11
11
  import QuantityPicker from '@salesforce/retail-react-app/app/components/quantity-picker/index'
@@ -34,7 +34,7 @@ describe('QuantityPicker', () => {
34
34
  await user.click(button)
35
35
  expect(input.value).toBe('4')
36
36
  })
37
- test('hitting enter/space on plus increments value', async () => {
37
+ test('typing enter/space on plus increments value', async () => {
38
38
  const user = userEvent.setup()
39
39
  renderWithProviders(<MockComponent />)
40
40
  const input = screen.getByRole('spinbutton')
@@ -44,7 +44,19 @@ describe('QuantityPicker', () => {
44
44
  await user.type(button, '{space}')
45
45
  expect(input.value).toBe('7')
46
46
  })
47
- test('hitting space on minus decrements value', async () => {
47
+
48
+ test('keydown enter/space on plus increments value', async () => {
49
+ const user = userEvent.setup()
50
+ renderWithProviders(<MockComponent />)
51
+ const input = screen.getByRole('spinbutton')
52
+ const button = screen.getByText('+')
53
+ fireEvent.keyDown(button, {key: 'Enter'})
54
+ expect(input.value).toBe('6')
55
+ fireEvent.keyDown(button, {key: ' '})
56
+ expect(input.value).toBe('7')
57
+ })
58
+
59
+ test('typing space on minus decrements value', async () => {
48
60
  const user = userEvent.setup()
49
61
  renderWithProviders(<MockComponent />)
50
62
  const input = screen.getByRole('spinbutton')
@@ -54,6 +66,18 @@ describe('QuantityPicker', () => {
54
66
  await user.type(button, '{space}')
55
67
  expect(input.value).toBe('3')
56
68
  })
69
+
70
+ test('keydown enter/space on minus decrements value', async () => {
71
+ const user = userEvent.setup()
72
+ renderWithProviders(<MockComponent />)
73
+ const input = screen.getByRole('spinbutton')
74
+ const button = screen.getByText(MINUS)
75
+ fireEvent.keyDown(button, {key: 'Enter'})
76
+ expect(input.value).toBe('4')
77
+ fireEvent.keyDown(button, {key: ' '})
78
+ expect(input.value).toBe('3')
79
+ })
80
+
57
81
  test('plus button is tabbable', async () => {
58
82
  const user = userEvent.setup()
59
83
  renderWithProviders(<MockComponent />)
package/app/constants.js CHANGED
@@ -18,7 +18,7 @@ export const STALE_WHILE_REVALIDATE = 60 * 15 // 15 min
18
18
  export const DEFAULT_SEARCH_PARAMS = {limit: 25, offset: 0, sort: 'best-matches', refine: []}
19
19
  export const DEFAULT_LIMIT_VALUES = [25, 50, 100] // Page sizes
20
20
 
21
- //Constants for customer orders searching.
21
+ // Constants for customer orders searching.
22
22
  export const DEFAULT_ORDERS_SEARCH_PARAMS = {limit: 10, offset: 0, sort: 'best-matches', refine: []}
23
23
 
24
24
  // Constants for Search Component
@@ -27,11 +27,11 @@ export const RECENT_SEARCH_KEY = 'recent-search-key'
27
27
  export const RECENT_SEARCH_MIN_LENGTH = 3
28
28
 
29
29
  // Constants for product list page
30
- export const PRODUCT_LIST_IMAGE_VIEW_TYPE = 'large'
30
+ export const PRODUCT_LIST_IMAGE_VIEW_TYPE = 'medium'
31
31
  export const PRODUCT_LIST_SELECTABLE_ATTRIBUTE_ID = 'color'
32
32
 
33
33
  // Constants for product tile page
34
- export const PRODUCT_TILE_IMAGE_VIEW_TYPE = 'large'
34
+ export const PRODUCT_TILE_IMAGE_VIEW_TYPE = 'medium'
35
35
  export const PRODUCT_TILE_SELECTABLE_ATTRIBUTE_ID = 'color'
36
36
 
37
37
  // Constants for the Homepage's Shop Products section.
@@ -357,7 +357,7 @@ test('Allows customer to create an account', async () => {
357
357
  )
358
358
  })
359
359
 
360
- // TODO: investingate why this test is failing when running with other tests
360
+ // TODO: investigate why this test is failing when running with other tests
361
361
  // eslint-disable-next-line jest/no-disabled-tests
362
362
  test.skip('Allows customer to sign in to their account', async () => {
363
363
  const user = userEvent.setup()
@@ -10,7 +10,7 @@ import {MultiSiteContext} from '@salesforce/retail-react-app/app/contexts'
10
10
 
11
11
  /**
12
12
  * Custom React hook to get the function that returns usefule multi-site values, the site, the locale and
13
- * the funtion used to build URLs following the App configuration.
13
+ * the function used to build URLs following the App configuration.
14
14
  * @returns {{site, locale, buildUrl: (function(*, *, *): *)}}
15
15
  */
16
16
  const useMultiSite = () => {
@@ -17,10 +17,10 @@ const mockProductSearchItem = {
17
17
  {
18
18
  images: [
19
19
  {
20
- alt: 'Charcoal Single Pleat Wool Suit, , large',
20
+ alt: 'Charcoal Single Pleat Wool Suit, , medium',
21
21
  disBaseLink:
22
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw175c1a89/images/large/PG.33698RUBN4Q.CHARCWL.PZ.jpg',
23
- link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw175c1a89/images/large/PG.33698RUBN4Q.CHARCWL.PZ.jpg',
22
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw23283a20/images/medium/PG.33698RUBN4Q.CHARCWL.PZ.jpg',
23
+ link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw23283a20/images/medium/PG.33698RUBN4Q.CHARCWL.PZ.jpg',
24
24
  title: 'Charcoal Single Pleat Wool Suit, '
25
25
  },
26
26
  {
@@ -36,10 +36,10 @@ const mockProductSearchItem = {
36
36
  {
37
37
  images: [
38
38
  {
39
- alt: 'Charcoal Single Pleat Wool Suit, Charcoal, large',
39
+ alt: 'Charcoal Single Pleat Wool Suit, Charcoal, medium',
40
40
  disBaseLink:
41
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw175c1a89/images/large/PG.33698RUBN4Q.CHARCWL.PZ.jpg',
42
- link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw175c1a89/images/large/PG.33698RUBN4Q.CHARCWL.PZ.jpg',
41
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw23283a20/images/medium/PG.33698RUBN4Q.CHARCWL.PZ.jpg',
42
+ link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw23283a20/images/medium/PG.33698RUBN4Q.CHARCWL.PZ.jpg',
43
43
  title: 'Charcoal Single Pleat Wool Suit, Charcoal'
44
44
  },
45
45
  {
@@ -535,9 +535,9 @@ const mockStandardProductHit = {
535
535
  currency: 'GBP',
536
536
  hitType: 'product',
537
537
  image: {
538
- alt: 'Laptop Briefcase with wheels (37L), , large',
538
+ alt: 'Laptop Briefcase with wheels (37L), , medium',
539
539
  disBaseLink:
540
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7cb2d401/images/large/P0048_001.jpg',
540
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwcc8912f4/images/medium/P0048_001.jpg?sw=768&q=60',
541
541
  link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7cb2d401/images/large/P0048_001.jpg',
542
542
  title: 'Laptop Briefcase with wheels (37L), '
543
543
  },
@@ -578,9 +578,9 @@ const mockMasterProductHitWithOneVariant = {
578
578
  currency: 'GBP',
579
579
  hitType: 'master',
580
580
  image: {
581
- alt: 'Black Flat Front Wool Suit, , large',
581
+ alt: 'Black Flat Front Wool Suit, , medium',
582
582
  disBaseLink:
583
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3d8972fe/images/large/PG.52001DAN84Q.BLACKWL.PZ.jpg',
583
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw5aa46219/images/medium/PG.52001DAN84Q.BLACKWL.PZ.jpg',
584
584
  link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3d8972fe/images/large/PG.52001DAN84Q.BLACKWL.PZ.jpg',
585
585
  title: 'Black Flat Front Wool Suit, '
586
586
  },
@@ -704,9 +704,9 @@ const mockMasterProductHitWithMultipleVariants = {
704
704
  currency: 'GBP',
705
705
  hitType: 'master',
706
706
  image: {
707
- alt: 'Long Sleeve Embellished Boat Neck Top, , large',
707
+ alt: 'Long Sleeve Embellished Boat Neck Top, , medium',
708
708
  disBaseLink:
709
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e4c00a0/images/large/PG.10217069.JJ5QZXX.PZ.jpg',
709
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed56b2da/images/medium/PG.10217069.JJ5QZXX.PZ.jpg',
710
710
  link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e4c00a0/images/large/PG.10217069.JJ5QZXX.PZ.jpg',
711
711
  title: 'Long Sleeve Embellished Boat Neck Top, '
712
712
  },
@@ -714,9 +714,9 @@ const mockMasterProductHitWithMultipleVariants = {
714
714
  {
715
715
  images: [
716
716
  {
717
- alt: 'Long Sleeve Embellished Boat Neck Top, , large',
717
+ alt: 'Long Sleeve Embellished Boat Neck Top, , medium',
718
718
  disBaseLink:
719
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e4c00a0/images/large/PG.10217069.JJ5QZXX.PZ.jpg',
719
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed56b2da/images/medium/PG.10217069.JJ5QZXX.PZ.jpg',
720
720
  link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e4c00a0/images/large/PG.10217069.JJ5QZXX.PZ.jpg',
721
721
  title: 'Long Sleeve Embellished Boat Neck Top, '
722
722
  },
@@ -733,9 +733,9 @@ const mockMasterProductHitWithMultipleVariants = {
733
733
  {
734
734
  images: [
735
735
  {
736
- alt: 'Long Sleeve Embellished Boat Neck Top, Begonia Pink, large',
736
+ alt: 'Long Sleeve Embellished Boat Neck Top, Begonia Pink, medium',
737
737
  disBaseLink:
738
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e4c00a0/images/large/PG.10217069.JJ5QZXX.PZ.jpg',
738
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwed56b2da/images/medium/PG.10217069.JJ5QZXX.PZ.jpg',
739
739
  link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw7e4c00a0/images/large/PG.10217069.JJ5QZXX.PZ.jpg',
740
740
  title: 'Long Sleeve Embellished Boat Neck Top, Begonia Pink'
741
741
  },
@@ -762,9 +762,9 @@ const mockMasterProductHitWithMultipleVariants = {
762
762
  {
763
763
  images: [
764
764
  {
765
- alt: 'Long Sleeve Embellished Boat Neck Top, Grey Heather, large',
765
+ alt: 'Long Sleeve Embellished Boat Neck Top, Grey Heather, medium',
766
766
  disBaseLink:
767
- 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3255ea4c/images/large/PG.10217069.JJ908XX.PZ.jpg',
767
+ 'https://edge.disstg.commercecloud.salesforce.com/dw/image/v2/ZZRF_001/on/demandware.static/-/Sites-apparel-m-catalog/default/dwc9ce7da9/images/medium/PG.10217069.JJ908XX.PZ.jpg',
768
768
  link: 'https://zzrf-001.dx.commercecloud.salesforce.com/on/demandware.static/-/Sites-apparel-m-catalog/default/dw3255ea4c/images/large/PG.10217069.JJ908XX.PZ.jpg',
769
769
  title: 'Long Sleeve Embellished Boat Neck Top, Grey Heather'
770
770
  },
@@ -140,8 +140,8 @@ describe('Render and logs out', function () {
140
140
  expect(logOutIcons[1]).toHaveAttribute('aria-hidden', 'true')
141
141
  })
142
142
 
143
- await user.click(screen.getAllByText(/Log Out/)[0])
144
143
  useCustomerType.mockReturnValue({isRegistered: false, isGuest: true})
144
+ await user.click(screen.getAllByText(/Log Out/)[0])
145
145
 
146
146
  await waitFor(() => {
147
147
  expect(window.location.pathname).toBe(`${expectedBasePath}/login`)
@@ -28,6 +28,7 @@ import {
28
28
  baskets as mockBaskets,
29
29
  products as mockProducts
30
30
  } from '@salesforce/retail-react-app/app/pages/cart/cart.mock'
31
+ import userEvent from '@testing-library/user-event'
31
32
 
32
33
  const mockProduct = {
33
34
  ...mockVariant,
@@ -549,6 +550,128 @@ describe('Update this is a gift option', function () {
549
550
  })
550
551
  })
551
552
  })
553
+ describe('Update this is not a gift option', function () {
554
+ beforeEach(() => {
555
+ global.server.use(
556
+ rest.patch('*/baskets/:basketId/items/:itemId', (req, res, ctx) => {
557
+ const basket = mockCustomerBaskets.baskets[0]
558
+ const updatedBasket = {
559
+ ...basket,
560
+ productItems: [
561
+ {
562
+ adjustedTax: 2.93,
563
+ basePrice: 61.43,
564
+ bonusProductLineItem: false,
565
+ gift: false,
566
+ itemId: '4a9af0a24fe46c3f6d8721b371',
567
+ itemText: 'Belted Cardigan With Studs',
568
+ price: 61.43,
569
+ priceAfterItemDiscount: 61.43,
570
+ priceAfterOrderDiscount: 61.43,
571
+ productId: '701642889830M',
572
+ productName: 'Belted Cardigan With Studs',
573
+ quantity: 2,
574
+ shipmentId: 'me',
575
+ tax: 2.93,
576
+ taxBasis: 61.43,
577
+ taxClassId: 'standard',
578
+ taxRate: 0.05
579
+ }
580
+ ]
581
+ }
582
+ return res(ctx.json(updatedBasket))
583
+ }),
584
+ rest.get('*/customers/:customerId/baskets', (req, res, ctx) => {
585
+ return res(
586
+ ctx.delay(0),
587
+ ctx.json({
588
+ baskets: [
589
+ {
590
+ ...mockCustomerBaskets.baskets[0],
591
+ productItems: [
592
+ {
593
+ adjustedTax: 2.93,
594
+ basePrice: 61.43,
595
+ bonusProductLineItem: false,
596
+ gift: true,
597
+ itemId: '4a9af0a24fe46c3f6d8721b371',
598
+ itemText: 'Belted Cardigan With Studs',
599
+ price: 61.43,
600
+ priceAfterItemDiscount: 61.43,
601
+ priceAfterOrderDiscount: 61.43,
602
+ productId: '701642889830M',
603
+ productName: 'Belted Cardigan With Studs',
604
+ quantity: 2,
605
+ shipmentId: 'me',
606
+ tax: 2.93,
607
+ taxBasis: 61.43,
608
+ taxClassId: 'standard',
609
+ taxRate: 0.05
610
+ }
611
+ ]
612
+ }
613
+ ],
614
+ total: 1
615
+ })
616
+ )
617
+ })
618
+ )
619
+ })
620
+ test('can update cart item when user unchecks the gift checkbox', async () => {
621
+ const {user} = renderWithProviders(<Cart />)
622
+ await waitFor(() => {
623
+ expect(screen.getByTestId('sf-cart-container')).toBeInTheDocument()
624
+ expect(screen.getByText(/Belted Cardigan With Studs/i)).toBeInTheDocument()
625
+
626
+ const cartItem = screen.getByTestId('sf-cart-item-701642889830M')
627
+ expect(cartItem).toBeInTheDocument()
628
+ })
629
+
630
+ const giftCheckbox = screen.getByRole('checkbox')
631
+ expect(giftCheckbox).toBeChecked()
632
+ await user.click(giftCheckbox)
633
+ global.server.use(
634
+ rest.get('*/customers/:customerId/baskets', (req, res, ctx) => {
635
+ return res.once(
636
+ ctx.delay(0),
637
+ ctx.json({
638
+ baskets: [
639
+ {
640
+ ...mockCustomerBaskets.baskets[0],
641
+ productItems: [
642
+ {
643
+ adjustedTax: 2.93,
644
+ basePrice: 61.43,
645
+ bonusProductLineItem: false,
646
+ gift: false,
647
+ itemId: '4a9af0a24fe46c3f6d8721b371',
648
+ itemText: 'Belted Cardigan With Studs',
649
+ price: 61.43,
650
+ priceAfterItemDiscount: 61.43,
651
+ priceAfterOrderDiscount: 61.43,
652
+ productId: '701642889830M',
653
+ productName: 'Belted Cardigan With Studs',
654
+ quantity: 2,
655
+ shipmentId: 'me',
656
+ tax: 2.93,
657
+ taxBasis: 61.43,
658
+ taxClassId: 'standard',
659
+ taxRate: 0.05
660
+ }
661
+ ]
662
+ }
663
+ ],
664
+ total: 1
665
+ })
666
+ )
667
+ })
668
+ )
669
+
670
+ await waitFor(() => {
671
+ expect(giftCheckbox).not.toBeChecked()
672
+ })
673
+ })
674
+ })
552
675
 
553
676
  describe('Product bundles', () => {
554
677
  beforeEach(() => {
@@ -796,3 +919,26 @@ describe('Unavailable products tests', function () {
796
919
  })
797
920
  })
798
921
  })
922
+
923
+ test('shows error toast when updating cart item fails', async () => {
924
+ // Mock the update item API to fail
925
+ global.server.use(
926
+ rest.patch('*/baskets/:basketId/items/:itemId', (req, res, ctx) => {
927
+ return res(ctx.status(500))
928
+ })
929
+ )
930
+
931
+ renderWithProviders(<Cart />)
932
+ await waitFor(() => expect(screen.getByTestId('sf-cart-container')).toBeInTheDocument())
933
+
934
+ // Find the first "Edit" button and click it to open the edit modal
935
+ const editButtons = await screen.findAllByRole('button', {name: /edit/i})
936
+ await userEvent.click(editButtons[0])
937
+
938
+ // In the modal, find and click the "Update" or "Save" button (adjust selector as needed)
939
+ const updateButton = await screen.findByRole('button', {name: /update|save/i})
940
+ await userEvent.click(updateButton)
941
+
942
+ // Wait for the error toast to appear
943
+ await waitFor(() => expect(screen.getByText(/something went wrong/i)).toBeInTheDocument())
944
+ })
@@ -14,3 +14,10 @@ test('renders component', () => {
14
14
  renderWithProviders(<CheckoutFooter />)
15
15
  expect(screen.getByRole('link', {name: 'Shipping'})).toBeInTheDocument()
16
16
  })
17
+
18
+ test('displays copyright message with current year', () => {
19
+ renderWithProviders(<CheckoutFooter />)
20
+ const currentYear = new Date().getFullYear()
21
+ const copyrightText = `© ${currentYear} Salesforce or its affiliates. All rights reserved. This is a demo store only. Orders made WILL NOT be processed.`
22
+ expect(screen.getByText(copyrightText)).toBeInTheDocument()
23
+ })
@@ -99,6 +99,39 @@ describe('Logging in tests', function () {
99
99
  )
100
100
  })
101
101
 
102
+ test('Shows inline error when email is empty', async () => {
103
+ const {user} = renderWithProviders(<MockedComponent />, {
104
+ wrapperProps: {
105
+ siteAlias: 'uk',
106
+ locale: {id: 'en-GB'},
107
+ appConfig: mockConfig.app,
108
+ bypassAuth: false
109
+ }
110
+ })
111
+
112
+ // Only fill password, leave email empty
113
+ await user.type(screen.getByLabelText('Password'), 'Password!1')
114
+ // Try to submit the form
115
+ await user.click(screen.getByText(/sign in/i))
116
+ expect(await screen.findByText(/Please enter your email address\./i)).toBeInTheDocument()
117
+ })
118
+
119
+ test('Shows inline error when password is empty', async () => {
120
+ const {user} = renderWithProviders(<MockedComponent />, {
121
+ wrapperProps: {
122
+ siteAlias: 'uk',
123
+ locale: {id: 'en-GB'},
124
+ appConfig: mockConfig.app,
125
+ bypassAuth: false
126
+ }
127
+ })
128
+ // Only fill email, leave password empty
129
+ await user.type(screen.getByLabelText('Email'), 'customer@test.com')
130
+ // Try to submit the form
131
+ await user.click(screen.getByText(/sign in/i))
132
+ expect(await screen.findByText(/Please enter your password\./i)).toBeInTheDocument()
133
+ })
134
+
102
135
  test('Allows customer to sign in to their account', async () => {
103
136
  const {user} = renderWithProviders(<MockedComponent />, {
104
137
  wrapperProps: {
@@ -554,7 +554,7 @@ const ProductList = (props) => {
554
554
  </Stack>
555
555
  <Box>
556
556
  <SimpleGrid
557
- columns={[2, 2, 3, 3]}
557
+ columns={[2, 2, 4, 5]}
558
558
  spacingX={4}
559
559
  spacingY={{base: 12, lg: 16}}
560
560
  >
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@salesforce/retail-react-app",
3
- "version": "6.1.0",
3
+ "version": "7.0.0-nightly-20250526080400",
4
4
  "license": "See license in LICENSE",
5
5
  "author": "cc-pwa-kit@salesforce.com",
6
6
  "ccExtensibility": {
@@ -46,10 +46,10 @@
46
46
  "@loadable/component": "^5.15.3",
47
47
  "@peculiar/webcrypto": "^1.4.2",
48
48
  "@salesforce/cc-datacloud-typescript": "1.1.2",
49
- "@salesforce/commerce-sdk-react": "3.3.0",
50
- "@salesforce/pwa-kit-dev": "3.10.0",
51
- "@salesforce/pwa-kit-react-sdk": "3.10.0",
52
- "@salesforce/pwa-kit-runtime": "3.10.0",
49
+ "@salesforce/commerce-sdk-react": "3.4.0-nightly-20250526080400",
50
+ "@salesforce/pwa-kit-dev": "3.11.0-nightly-20250526080400",
51
+ "@salesforce/pwa-kit-react-sdk": "3.11.0-nightly-20250526080400",
52
+ "@salesforce/pwa-kit-runtime": "3.11.0-nightly-20250526080400",
53
53
  "@tanstack/react-query": "^4.28.0",
54
54
  "@tanstack/react-query-devtools": "^4.29.1",
55
55
  "@testing-library/dom": "^9.0.1",
@@ -107,5 +107,5 @@
107
107
  "maxSize": "328 kB"
108
108
  }
109
109
  ],
110
- "gitHead": "da9ff1c4b28222ba4d27661be61c18f43192c83d"
110
+ "gitHead": "f7cdce47d5702c370abf36a0a639bd6e7fee89c6"
111
111
  }