-
Notifications
You must be signed in to change notification settings - Fork 149
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
fix(ecommerce-app-base): drag-n-drop bug in refetch logic that was ca…
…using infinite loop (#7944)
- Loading branch information
1 parent
8fd5efd
commit e5dcf5a
Showing
6 changed files
with
197 additions
and
63 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
116 changes: 116 additions & 0 deletions
116
packages/ecommerce-app-base/src/Editor/SortableComponent.spec.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
import React from 'react'; | ||
import { render, cleanup, waitFor } from '@testing-library/react'; | ||
import { SortableComponent } from './SortableComponent'; | ||
import { makeSdkMock, productsList } from '../__mocks__'; | ||
import { FieldAppSDK } from '@contentful/app-sdk'; | ||
import { ProductPreviewsFn } from '../types'; | ||
|
||
const mockSdk = makeSdkMock(); | ||
const skus = ['M0E20130820E90Z', 'A0E2300FX102203', 'M0E21300900DZN7']; | ||
const mockConfig = {}; | ||
const mockSkuType = 'mock-sku-type'; | ||
|
||
describe('SortableComponent', () => { | ||
let mockFetchProductPreviews: ProductPreviewsFn; | ||
|
||
beforeEach(() => { | ||
// eslint-disable-next-line @typescript-eslint/ban-ts-comment | ||
// @ts-ignore - typescript is upset because jest.fn() returns a type different than ProductPreviewsFn | ||
mockFetchProductPreviews = jest.fn().mockImplementation(() => { | ||
return Promise.resolve(productsList); | ||
}); | ||
}); | ||
|
||
afterEach(() => { | ||
cleanup(); | ||
jest.resetAllMocks(); | ||
}); | ||
|
||
it('calls `fetchProductPreviews()` to retrieve list of products for associated skus', async () => { | ||
waitFor(() => { | ||
render( | ||
<SortableComponent | ||
sdk={mockSdk as unknown as FieldAppSDK} | ||
disabled={false} | ||
config={mockConfig} | ||
skus={skus} | ||
onChange={jest.fn()} | ||
fetchProductPreviews={mockFetchProductPreviews} | ||
skuType={mockSkuType} | ||
/> | ||
); | ||
}); | ||
|
||
expect(mockFetchProductPreviews).toHaveBeenCalledTimes(1); | ||
expect(mockFetchProductPreviews).toHaveBeenLastCalledWith(skus, mockConfig, mockSkuType); | ||
}); | ||
|
||
it('refetches list of products for associated skus, when skus are added/removed', async () => { | ||
const newSkus = [...skus, 'new-sku-item']; | ||
|
||
await waitFor(() => { | ||
// initial mount with original skus | ||
const { rerender } = render( | ||
<SortableComponent | ||
sdk={mockSdk as unknown as FieldAppSDK} | ||
disabled={false} | ||
config={mockConfig} | ||
skus={skus} | ||
onChange={jest.fn()} | ||
fetchProductPreviews={mockFetchProductPreviews} | ||
skuType={mockSkuType} | ||
/> | ||
); | ||
|
||
// rerender with additional sku | ||
rerender( | ||
<SortableComponent | ||
skus={newSkus} | ||
sdk={mockSdk as unknown as FieldAppSDK} | ||
disabled={false} | ||
config={mockConfig} | ||
onChange={jest.fn()} | ||
fetchProductPreviews={mockFetchProductPreviews} | ||
skuType={mockSkuType} | ||
/> | ||
); | ||
}); | ||
|
||
expect(mockFetchProductPreviews).toHaveBeenCalledTimes(2); | ||
expect(mockFetchProductPreviews).toHaveBeenLastCalledWith(newSkus, mockConfig, mockSkuType); | ||
}); | ||
|
||
it('does NOT refetch list of products when skus have simply been reordered', async () => { | ||
const reorderedSkus = [skus[1], skus[0], skus[2]]; | ||
|
||
await waitFor(() => { | ||
// initial mount with original skus | ||
const { rerender } = render( | ||
<SortableComponent | ||
sdk={mockSdk as unknown as FieldAppSDK} | ||
disabled={false} | ||
config={mockConfig} | ||
skus={skus} | ||
onChange={jest.fn()} | ||
fetchProductPreviews={mockFetchProductPreviews} | ||
skuType={mockSkuType} | ||
/> | ||
); | ||
|
||
// rerender with new skus, just reordered | ||
rerender( | ||
<SortableComponent | ||
skus={reorderedSkus} | ||
sdk={mockSdk as unknown as FieldAppSDK} | ||
disabled={false} | ||
config={mockConfig} | ||
onChange={jest.fn()} | ||
fetchProductPreviews={mockFetchProductPreviews} | ||
skuType={mockSkuType} | ||
/> | ||
); | ||
|
||
expect(mockFetchProductPreviews).toHaveBeenCalledTimes(1); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,3 @@ | ||
export { products, productsList } from './products'; | ||
export { makeSdkMock } from './mockSdk'; | ||
export { mockContentTypes } from './mockContentTypes'; |
28 changes: 28 additions & 0 deletions
28
packages/ecommerce-app-base/src/__mocks__/mockContentTypes.ts
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
export const mockContentTypes = [ | ||
{ | ||
sys: { id: 'ct1' }, | ||
name: 'CT1', | ||
fields: [ | ||
{ id: 'product_x', name: 'Product X', type: 'Symbol' }, | ||
{ id: 'y', name: 'Y', type: 'Object' }, | ||
], | ||
}, | ||
{ | ||
sys: { id: 'ct2' }, | ||
name: 'CT2', | ||
fields: [ | ||
{ id: 'foo', name: 'FOO', type: 'Text' }, | ||
{ id: 'z', name: 'Z', type: 'Array', items: { type: 'Symbol' } }, | ||
], | ||
}, | ||
{ | ||
sys: { id: 'ct3' }, | ||
name: 'CT3', | ||
fields: [ | ||
{ id: 'bar', name: 'BAR', type: 'Object' }, | ||
{ id: 'baz', name: 'BAZ', type: 'Object' }, | ||
{ id: 'product_d', name: 'Product D', type: 'Array', items: { type: 'Symbol' } }, | ||
{ id: 'product_a', name: 'Product A', type: 'Symbol' }, | ||
], | ||
}, | ||
]; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,22 @@ | ||
import { mockContentTypes } from './mockContentTypes'; | ||
|
||
export const makeSdkMock = () => ({ | ||
ids: { | ||
app: 'some-app', | ||
}, | ||
hostnames: { | ||
webapp: 'app.contentful.com', | ||
}, | ||
space: { | ||
getContentTypes: jest.fn().mockResolvedValue({ items: mockContentTypes }), | ||
getEditorInterfaces: jest.fn().mockResolvedValue({ items: [] }), | ||
}, | ||
app: { | ||
setReady: jest.fn(), | ||
getParameters: jest.fn().mockResolvedValue(null), | ||
onConfigure: jest.fn().mockReturnValue(undefined), | ||
}, | ||
notifier: { | ||
error: (msg: string) => console.log(`[mockSdk] error: ${msg}`), | ||
}, | ||
}); |