Skip to content

Commit 178d9da

Browse files
committed
fix(catalyst): Fix WishlistDetails page from exceeding complexity limit, fix e2e tests
1 parent 52ee85e commit 178d9da

7 files changed

Lines changed: 148 additions & 20 deletions

File tree

.changeset/silent-pianos-search.md

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
---
2+
"@bigcommerce/catalyst-core": patch
3+
---
4+
5+
Fix WishlistDetails page from exceeding GraphQL complexity limit, and fix wishlist e2e tests
6+
7+
## Migration
8+
9+
### Step 1: Update wishlist GraphQL fragments
10+
11+
In `core/components/wishlist/fragment.ts`, replace the `WishlistItemProductFragment` to use explicit fields instead of `ProductCardFragment`:
12+
13+
```typescript
14+
export const WishlistItemProductFragment = graphql(
15+
`
16+
fragment WishlistItemProductFragment on Product {
17+
entityId
18+
name
19+
defaultImage {
20+
altText
21+
url: urlTemplate(lossy: true)
22+
}
23+
path
24+
brand {
25+
name
26+
path
27+
}
28+
reviewSummary {
29+
numberOfReviews
30+
averageRating
31+
}
32+
sku
33+
showCartAction
34+
inventory {
35+
isInStock
36+
}
37+
availabilityV2 {
38+
status
39+
}
40+
...PricingFragment
41+
}
42+
`,
43+
[PricingFragment],
44+
);
45+
```
46+
47+
Remove `ProductCardFragment` from all fragment dependencies in the same file.
48+
49+
### Step 2: Update product card transformer
50+
51+
In `core/data-transformers/product-card-transformer.ts`:
52+
53+
1. Import the `WishlistItemProductFragment`:
54+
```typescript
55+
import { WishlistItemProductFragment } from '~/components/wishlist/fragment';
56+
```
57+
58+
2. Update the `singleProductCardTransformer` function signature to accept both fragment types:
59+
```typescript
60+
product: ResultOf<typeof ProductCardFragment | typeof WishlistItemProductFragment>
61+
```
62+
63+
3. Add a conditional check for the `inventoryMessage` field:
64+
```typescript
65+
inventoryMessage:
66+
'variants' in product
67+
? getInventoryMessage(product, outOfStockMessage, showBackorderMessage)
68+
: undefined,
69+
```
70+
71+
4. Update the `productCardTransformer` function signature similarly:
72+
```typescript
73+
products: Array<ResultOf<typeof ProductCardFragment | typeof WishlistItemProductFragment>>
74+
```
75+
76+
### Step 3: Fix wishlist e2e tests
77+
78+
In `core/tests/ui/e2e/account/wishlists.spec.ts`, update label selectors to use `{ exact: true }` for specificity:
79+
80+
Update all locators for the wishlist name input selectors:
81+
```diff
82+
- page.getByLabel(t('Form.nameLabel'))
83+
+ page.getByLabel(t('Form.nameLabel'), { exact: true })
84+
```
85+
86+
### Step 4: Fix mobile wishlist e2e tests
87+
88+
In `core/tests/ui/e2e/account/wishlists.mobile.spec.ts`, update translation calls to use namespace prefixes:
89+
90+
1. Update the translation initialization:
91+
```diff
92+
- const t = await getTranslations('Account.Wishlist');
93+
+ const t = await getTranslations();
94+
```
95+
96+
2. Update all translation keys to include the namespace:
97+
```diff
98+
- await locator.getByRole('button', { name: t('actionsTitle') }).click();
99+
- await page.getByRole('menuitem', { name: t('share') }).click();
100+
+ await locator.getByRole('button', { name: t('Wishlist.actionsTitle') }).click();
101+
+ await page.getByRole('menuitem', { name: t('Wishlist.share') }).click();
102+
```
103+
104+
```diff
105+
- await expect(page.getByText(t('shareSuccess'))).toBeVisible();
106+
+ await expect(page.getByText(t('Wishlist.shareSuccess'))).toBeVisible();
107+
```
108+

core/components/wishlist/fragment.ts

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,25 @@
11
import { PaginationFragment } from '~/client/fragments/pagination';
2+
import { PricingFragment } from '~/client/fragments/pricing';
23
import { graphql } from '~/client/graphql';
3-
import { ProductCardFragment } from '~/components/product-card/fragment';
44

55
export const WishlistItemProductFragment = graphql(
66
`
77
fragment WishlistItemProductFragment on Product {
8-
...ProductCardFragment
8+
entityId
9+
name
10+
defaultImage {
11+
altText
12+
url: urlTemplate(lossy: true)
13+
}
14+
path
15+
brand {
16+
name
17+
path
18+
}
19+
reviewSummary {
20+
numberOfReviews
21+
averageRating
22+
}
923
sku
1024
showCartAction
1125
inventory {
@@ -14,9 +28,10 @@ export const WishlistItemProductFragment = graphql(
1428
availabilityV2 {
1529
status
1630
}
31+
...PricingFragment
1732
}
1833
`,
19-
[ProductCardFragment],
34+
[PricingFragment],
2035
);
2136

2237
export const WishlistItemFragment = graphql(
@@ -30,7 +45,7 @@ export const WishlistItemFragment = graphql(
3045
}
3146
}
3247
`,
33-
[WishlistItemProductFragment, ProductCardFragment],
48+
[WishlistItemProductFragment],
3449
);
3550

3651
export const WishlistFragment = graphql(
@@ -52,7 +67,7 @@ export const WishlistFragment = graphql(
5267
}
5368
}
5469
`,
55-
[WishlistItemFragment, ProductCardFragment],
70+
[WishlistItemFragment],
5671
);
5772

5873
export const WishlistsFragment = graphql(
@@ -68,7 +83,7 @@ export const WishlistsFragment = graphql(
6883
}
6984
}
7085
`,
71-
[WishlistFragment, ProductCardFragment, PaginationFragment],
86+
[WishlistFragment, PaginationFragment],
7287
);
7388

7489
export const WishlistPaginatedItemsFragment = graphql(
@@ -93,7 +108,7 @@ export const WishlistPaginatedItemsFragment = graphql(
93108
}
94109
}
95110
`,
96-
[WishlistItemFragment, ProductCardFragment, PaginationFragment],
111+
[WishlistItemFragment, PaginationFragment],
97112
);
98113

99114
export const PublicWishlistFragment = graphql(
@@ -117,5 +132,5 @@ export const PublicWishlistFragment = graphql(
117132
}
118133
}
119134
`,
120-
[WishlistItemFragment, ProductCardFragment, PaginationFragment],
135+
[WishlistItemFragment, PaginationFragment],
121136
);

core/components/wishlist/modals/new.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ export const NewWishlistModal = ({
3232
onChange={(e) => {
3333
defaultValue.current = e.target.value;
3434
}}
35+
required
3536
/>
3637
{state.lastResult?.status === 'error' && (
3738
<div className="mt-4">

core/components/wishlist/modals/rename.tsx

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ export const RenameWishlistModal = ({
3333
onChange={(e) => {
3434
defaultValue.current = e.target.value;
3535
}}
36+
required
3637
/>
3738
{state.lastResult?.status === 'error' && (
3839
<div className="mt-4">

core/data-transformers/product-card-transformer.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import { getFormatter } from 'next-intl/server';
55
import { Product } from '@/vibes/soul/primitives/product-card';
66
import { ExistingResultType } from '~/client/util';
77
import { ProductCardFragment } from '~/components/product-card/fragment';
8+
import { WishlistItemProductFragment } from '~/components/wishlist/fragment';
89

910
import { pricesTransformer } from './prices-transformer';
1011

@@ -46,7 +47,7 @@ const getInventoryMessage = (
4647
};
4748

4849
export const singleProductCardTransformer = (
49-
product: ResultOf<typeof ProductCardFragment>,
50+
product: ResultOf<typeof ProductCardFragment | typeof WishlistItemProductFragment>,
5051
format: ExistingResultType<typeof getFormatter>,
5152
outOfStockMessage?: string,
5253
showBackorderMessage?: boolean,
@@ -62,12 +63,15 @@ export const singleProductCardTransformer = (
6263
subtitle: product.brand?.name ?? undefined,
6364
rating: product.reviewSummary.averageRating,
6465
numberOfReviews: product.reviewSummary.numberOfReviews,
65-
inventoryMessage: getInventoryMessage(product, outOfStockMessage, showBackorderMessage),
66+
inventoryMessage:
67+
'variants' in product
68+
? getInventoryMessage(product, outOfStockMessage, showBackorderMessage)
69+
: undefined,
6670
};
6771
};
6872

6973
export const productCardTransformer = (
70-
products: Array<ResultOf<typeof ProductCardFragment>>,
74+
products: Array<ResultOf<typeof ProductCardFragment | typeof WishlistItemProductFragment>>,
7175
format: ExistingResultType<typeof getFormatter>,
7276
outOfStockMessage?: string,
7377
showBackorderMessage?: boolean,

core/tests/ui/e2e/account/wishlists.mobile.spec.ts

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,21 @@ test('Share button calls navigator.share with the correct URL', async ({ page, c
2828
};
2929
});
3030

31-
const t = await getTranslations('Wishlist');
31+
const t = await getTranslations();
3232
const { id: customerId } = await customer.login();
3333
const { name, token } = await customer.createWishlist({
3434
customerId,
3535
isPublic: true,
3636
});
3737

3838
await page.goto('/account/wishlists/');
39-
await expect(page.getByRole('heading', { name: t('title'), exact: true })).toBeVisible();
4039

4140
const locator = page.getByRole('region', { name });
4241

43-
await locator.getByRole('button', { name: t('actionsTitle') }).click();
44-
await page.getByRole('menuitem', { name: t('share') }).click();
42+
await locator.getByRole('button', { name: t('Wishlist.actionsTitle') }).click();
43+
await page.getByRole('menuitem', { name: t('Wishlist.share') }).click();
4544

46-
await expect(page.getByText(t('shareSuccess'))).toBeVisible();
45+
await expect(page.getByText(t('Wishlist.shareSuccess'))).toBeVisible();
4746

4847
const expectedUrl = `${testEnv.PLAYWRIGHT_TEST_BASE_URL}/wishlist/${token}`;
4948

core/tests/ui/e2e/account/wishlists.spec.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -234,11 +234,11 @@ test.describe('Wishlist actions menu', () => {
234234
page.getByRole('heading', { name: t('Modal.renameTitle', { name }) }),
235235
).toBeVisible();
236236

237-
await expect(page.getByLabel(t('Form.nameLabel'))).toHaveValue(name);
237+
await expect(page.getByLabel(t('Form.nameLabel'), { exact: true })).toHaveValue(name);
238238

239239
const newName = `${name} (renamed)`;
240240

241-
await page.getByLabel(t('Form.nameLabel')).fill(newName);
241+
await page.getByLabel(t('Form.nameLabel'), { exact: true }).fill(newName);
242242
await page.getByRole('button', { name: t('Modal.save') }).click();
243243

244244
await expect(page.getByText(t('Result.updateSuccess'))).toBeVisible();
@@ -258,7 +258,7 @@ test.describe('Wishlist actions menu', () => {
258258
await locator.getByRole('button', { name: t('actionsTitle') }).click();
259259
await page.getByRole('menuitem', { name: t('rename') }).click();
260260

261-
await page.getByLabel(t('Form.nameLabel')).fill('');
261+
await page.getByLabel(t('Form.nameLabel'), { exact: true }).fill('');
262262
await page.getByRole('button', { name: t('Modal.save') }).click();
263263

264264
await expect(page.getByText(t('Errors.nameRequired'))).toBeVisible();
@@ -282,7 +282,7 @@ test.describe('Wishlist actions menu', () => {
282282

283283
await locator.getByRole('button', { name: t('actionsTitle') }).click();
284284
await page.getByRole('menuitem', { name: t('rename') }).click();
285-
await page.getByLabel(t('Form.nameLabel')).fill(`${name} (renamed)`);
285+
await page.getByLabel(t('Form.nameLabel'), { exact: true }).fill(`${name} (renamed)`);
286286
await page.getByRole('button', { name: t('Modal.save') }).click();
287287

288288
await expect(page.getByText(t('Errors.unauthorized'))).toBeVisible();

0 commit comments

Comments
 (0)