Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Testing product sets - @W-12414116@ #1003

Merged
merged 23 commits into from
Feb 27, 2023
Merged

Conversation

vmarta
Copy link
Contributor

@vmarta vmarta commented Feb 24, 2023

We'd like to add tests to verify the functionality of product sets in our retail-react-app.

Types of Changes

  • Bug fix (non-breaking change that fixes an issue)
  • New feature (non-breaking change that adds functionality)
  • Documentation update
  • Breaking change (could cause existing functionality to not work as expected)
  • Other changes (non-breaking changes that does not fit any of the above)

Breaking changes include:

  • Removing a public function or component or prop
  • Adding a required argument to a function
  • Changing the data type of a function parameter or return value
  • Adding a new peer dependency to package.json

Changes

  • (change1)

How to Test-Drive This PR

  • (step1)

Checklists

General

  • Changes are covered by test cases
  • CHANGELOG.md updated with a short description of changes (not required for documentation updates)

Accessibility Compliance

You must check off all items in one of the follow two lists:

  • There are no changes to UI

or...

Localization

  • Changes include a UI text update in the Retail React App (which requires translation)

Comment on lines -20 to +22
<Modal data-testid={'sf-product-view-modal'} size="4xl" isOpen={isOpen} onClose={onClose}>
<Modal size="4xl" isOpen={isOpen} onClose={onClose}>
<ModalOverlay />
<ModalContent>
<ModalContent containerProps={{'data-testid': 'product-view-modal'}}>
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unlike other Chakra components, Modal does not accept any props you pass in. So to set the test id, using containerProps like this is one way to do it.

@@ -62,13 +62,18 @@ export const AddToCartModal = () => {
const intl = useIntl()
const basket = useBasket()
const size = useBreakpointValue({base: 'full', lg: '2xl', xl: '4xl'})
const {currency, productItems, productSubTotal, itemAccumulatedCount} = basket
const {currency, productItems, productSubTotal} = basket
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

itemAccumulatedCount is a duplicate, when we're calculating totalQuantity in the line below. Removing the duplicate is also aligned with what's happening in the hooks-integration branch.

behavior: 'smooth',
block: 'end'
})
if (ref.scrollIntoView) {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added this check because during testing, scrollIntoView is not available function.

)
}

const basketWithProductSet = {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Mocking this data inside this test file. I'm not sure what our approach is. Sometimes the mocked data is in separate file, sometimes not. I guess it depends on whether the data is going to be reused?

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, and I would add one more consideration: sometimes I separate the mock data just because it obscures the test and makes it harder to think about if the mock data is too long.

I would vote to move this out to a <test-name>.mock.js file and have it live in the same directory (similar naming convention keeps them adjacent in your IDE) just because it distracts from the test

test('render multi-product layout', async () => {
renderWithProviders(<MockedPageWithProductSet />)

await waitFor(() => {
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Technically, I don't need to wait here. But I'm foreseeing the time when the hooks-integration work is merged. By then, the page-level component would use hooks to fetch data. And this asynchronous behaviour would require calling waitFor.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking most things should await / waitFor for that reason. Not many of our components are "dumb" enough to not need the pattern

@@ -142,6 +161,7 @@ export const TestProviders = ({
value={addToCartModal}
>
{children}
<AddToCartModal />
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our TestProviders were missing this modal. We had the context in there (line 160), but not the modal to render yet.

@@ -2985,6 +2985,1625 @@ export const mockedCustomerProductListsDetails = {
total: 1
}

// This wishlist has more varied products
export const mockedCustomerProductListsDetails2 = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What happened to the generator function we were working on when we paired on this? Can you call out the specific bits that are different about this mock vs the other?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've moved this data into a separate file ("something.mock.js" format) that is in a more specific location than the "app/commerce-api/mock-data.js". Because the mocked data is intended to be used only for a particular test.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@vmarta I think my main question got missed here. I realize I didn't really specify this, but I'm mainly interested in "how big is the difference between the existing mock data and this new mock data". If they're only 30% similar, I can understand not using the mock generator function pattern. If they're 90% similar, I would expect we want to use the generator pattern.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@bfeister oh sorry, the new mocked data is completely different in this case.

@@ -560,18 +561,24 @@ const MOCK_PRODUCT = {
c_width: 'Z'
}

test('Renders AddToCartModal', () => {
test('Renders AddToCartModal with multiple products', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tend to make all tests use at least some amount of await / async / waitFor so I would make this an async function

}
]
}
const {getByText} = renderWithProviders(

renderWithProviders(
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would await renderWithProviders(

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Our renderWithProviders, which would call render from the testing library, is not an async method. It returns an object. So calling await here would not do anything.

iTerm2 2023-02-27 at 13 43 50

Copy link
Collaborator

@bfeister bfeister left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks good. I think we could do some cleanup around items I mentioned, but no "blockers". I can approve if urgent / time is short

Comment on lines +592 to +596
expect(screen.getAllByText(MOCK_PRODUCT.name)[0]).toBeInTheDocument()
expect(screen.getByRole('dialog', {name: /23 items added to cart/i})).toBeInTheDocument()

const numOfRowsRendered = screen.getAllByTestId('product-added').length
expect(numOfRowsRendered).toEqual(MOCK_DATA.itemsAdded.length)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I remember you saying something about "awaiting too many things" which I'm still a bit confused about. If you want all of the expect conditions to be true at the same time (usually true, I believe) then I would wrap this in waitFor(() => {})

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think I won't use the waitFor in this case (in the product-sets feature branch). Maybe it becomes necessary within the hooks-integration work.

After the component is rendered, I expect all those expect conditions to be true.. all at the same time, yup. Because one render of the component is enough. It won't be re-rendered by some data fetching or async operation.

})
})

test('the View Full Details button', () => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I realize it's unlikely to matter for such a tiny component, but I might still use async here and waitFor just to make sure the tests don't behave differently or fail in CI / different OS runtimes

const modal = screen.getByTestId('product-view-modal')
expect(modal).toBeVisible()
},
// Seems like rendering the modal takes a bit more time
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Awesome find! 🎉

)
}

const basketWithProductSet = {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, and I would add one more consideration: sometimes I separate the mock data just because it obscures the test and makes it harder to think about if the mock data is too long.

I would vote to move this out to a <test-name>.mock.js file and have it live in the same directory (similar naming convention keeps them adjacent in your IDE) just because it distracts from the test

test('render multi-product layout', async () => {
renderWithProviders(<MockedPageWithProductSet />)

await waitFor(() => {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm thinking most things should await / waitFor for that reason. Not many of our components are "dumb" enough to not need the pattern

Comment on lines 284 to 288
global.server.use(
rest.post('*/baskets/:basketId/items', (req, res, ctx) => {
return res(ctx.json(basketWithProductSet))
})
)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yunakim714 did we ever decide whether this pattern (doing setup outside of a beforeEach) can cause race conditions. Seems like this should be in beforeEach to be sure we avoid race conditions

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can scope this server.use in a describe block to avoid it being used by other tests

describe('add to cart test', () => {
   beforeEach(() => {
    // this will apply to the tests inside this describe block
     global.server.use()

   })
  test('something', () => {})
})

@vmarta vmarta marked this pull request as ready for review February 27, 2023 22:21
@vmarta vmarta requested a review from a team as a code owner February 27, 2023 22:21
@vmarta vmarta merged commit 56771cb into feature/product-sets Feb 27, 2023
@vmarta vmarta deleted the test-product-sets branch February 27, 2023 22:34
vmarta added a commit that referenced this pull request Mar 1, 2023
* Product sets: basic PLP (#878)

* Show product sets on PLP (alongside other products)

For now, the product sets would look the same as any other kinds of products.

* Parse the product type

* Add todo

* Ignore linting of this line for now

* Add "Starting at" label

* Product sets: basic pdp (@W-12301851@) (#897)

* Product set: render the children products with minimal UI

* No longer showing breadcrumbs in the child items

* Individual swatches now have correct `value`

There was a subtle bug where the `value` of the SwatchGroup was passed down to override all the values of the children Swatch components.

* 1st attempt at saving swatches state in the page url

* Avoid null in the url search params

* Some refactoring

* Add-to-cart now works for child items

* Show accordion for each child item

* Add todo

* Rename variable for accuracy

The child product itself is actually not a set, but is considered a set product.

* Optional argument

* Some refactoring and fix linting

* Helper hook for url search params

* Clean up

* Rename module for a specific use case (PDP)

* Show/hide add-to-cart buttons accordingly

* Modal shows product set data

* Parent product

* Variant's product image shows up in add-to-cart modal

* Rename variables for clarity

* Fix linting error

* Add horizontal rules to separate the parent and child items

* Tweak whitespace

* Localize the Starting At label

* Add todo

* Product sets: wishlist (@W-12301966@) (#917)

* Starting At label

* 1st attempt at updating wishlist for product sets

* "Select Options" → "View Options"

* Some refactoring

* Render add-all-to-cart button if necessary

* Better way to fetch `setProducts` data

* Switch to zzrf-009 temporarily

* For debugging purpose

I'll revert this commit later

* Simpler requirement

* Update what quantity meant for product set

* Some refactoring

* Add comment

* Add-to-wishlist for the individual child items

* Rename variable

* Update button text on PDP

* Update button text on Wishlist page

* Revert "Switch to zzrf-009 temporarily"

This reverts commit 484c8d8.

* PDP: show wishlist button on mobile

* Fix formatting issues (prettier)

* PR feedback

* Clearer as to which is product or variant

* Make it consistent with addToWishlist signature

* [Feature] Add `Add Set to Cart` Button for Product Sets (#931)

* Product sets: basic PLP (#878)

* Variant's product image shows up in add-to-cart modal

* Get modal showing multiple items added to cart

* Add scroll to after validation

Co-authored-by: Vincent Marta <[email protected]>

* Einstein API to support product sets (#962)

* verify viewcategory and viewsearch is working

* viewproduct event is sent for every child product

* addtocart event fires for all children when add set to cart

* parent set only sends id

* Design enhancements for product sets (#975)

* remove complete the set carousel

* remove complete the set translations

* sticky buttons on mobile view

* fix modal vertical margins on desktop

* move recos to modalbody and cleanup margins

* add dividers between product set items in mobile view

* fix margins so sticky buttons do not overlap with content

* add lazy loading of product set children product images

* remove height - does nothing

* bump build size to 55

* add conditional for complete the set and change loading var name

* lint

* revert translations

* Cleanup

* added to cart modal is scrollable and 90% of viewport

* remove dialogprops

* lint

* Testing product sets - @W-12414116@ (#1003)

* Product tile: test product set's price label

* Test product set rendering

* Test callbacks are called properly

* itemAccumulatedCount is redundant

* Test that modal can render multiple products

* Finally the add-to-cart modal shows up in testing

* Clean up

* Test error messages

* Test lazy loading of the child products

* Split a test into smaller ones

* Use `describe` to group all the related tests

* Update comment

* Test the wishlist and its primary action buttons

* Clean up

* Some refactoring

* Some refactoring

* Linting fix

* Update comment

* Test `getDisplayVariationValues`

* Test the usePDPSearchParams

* Tweak another test to pass

Not related to product sets, but needing CI to pass.

* Move mocked data into their own files

* Moving it to within `beforeEach`

---------

Co-authored-by: Ben Chypak <[email protected]>
Co-authored-by: yunakim714 <[email protected]>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants