Skip to content

Commit

Permalink
fix(headless, commerce): schema validation error when switching betwe…
Browse files Browse the repository at this point in the history
  • Loading branch information
fbeaudoincoveo authored Sep 23, 2024
1 parent 70d9378 commit 5808edb
Show file tree
Hide file tree
Showing 38 changed files with 332 additions and 178 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,7 @@ export function buildFacetGenerator(
const createFacetState = (facetResponseSelector: AnyFacetResponse) => {
const facetId = facetResponseSelector.facetId;
return getCoreFacetState(
facetId,
facetRequestSelector(getEngineState(), facetId),
facetResponseSelector,
isFacetLoadingResponseSelector
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -428,7 +428,9 @@ describe('CoreCommerceFacet', () => {
});

describe('#state', () => {
it('#facetId exposes the facetId', () => {
it('#facetId exposes the facetId passed as an option to the controller', () => {
state.commerceFacetSet = {};
options.facetResponseSelector = jest.fn();
expect(facet.state.facetId).toBe(facetId);
});

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -280,7 +280,12 @@ export function buildCoreCommerceFacet<
},

get state(): CoreCommerceFacetState<ValueResponse> {
return getCoreFacetState(getRequest(), getResponse(), getIsLoading());
return getCoreFacetState(
facetId,
getRequest(),
getResponse(),
getIsLoading()
);
},
};
}
Expand All @@ -307,6 +312,7 @@ const canShowLessValues = (request: AnyFacetRequest | undefined) => {
};

export const getCoreFacetState = <T extends AnyFacetValueResponse>(
facetId: string,
request: AnyFacetRequest | undefined,
response: AnyFacetResponse | undefined,
isLoading: boolean
Expand All @@ -315,7 +321,7 @@ export const getCoreFacetState = <T extends AnyFacetValueResponse>(
canShowLessValues: canShowLessValues(request),
canShowMoreValues: response?.moreValuesAvailable ?? false,
displayName: response?.displayName ?? '',
facetId: response?.facetId ?? '',
facetId: facetId,
field: response?.field ?? '',
hasActiveValues:
!response || response.type === 'hierarchical'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
buildMockBaseProduct,
} from '../../../test/mock-product';
import {buildFetchProductListingResponse} from '../../../test/mock-product-listing';
import {setContext, setView} from '../context/context-actions';
import {
fetchMoreProducts,
fetchProductListing,
Expand Down Expand Up @@ -314,4 +315,41 @@ describe('product-listing-slice', () => {
]);
});
});
it('on #setView, restores the initial state', () => {
state = {
error: {message: 'error', statusCode: 500, type: 'type'},
isLoading: true,
products: [buildMockProduct({ec_name: 'product1'})],
facets: [buildMockCommerceRegularFacetResponse()],
responseId: 'response-id',
requestId: 'request-id',
};

const finalState = productListingReducer(state, setView({url: '/'}));

expect(finalState).toEqual(getProductListingInitialState());
});

it('on #setContext, restores the initial state', () => {
state = {
error: {message: 'error', statusCode: 500, type: 'type'},
isLoading: true,
products: [buildMockProduct({ec_name: 'product1'})],
facets: [buildMockCommerceRegularFacetResponse()],
responseId: 'response-id',
requestId: 'request-id',
};

const finalState = productListingReducer(
state,
setContext({
country: 'CA',
currency: 'CAD',
language: 'fr',
view: {url: '/'},
})
);

expect(finalState).toEqual(getProductListingInitialState());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
} from '../../../api/commerce/common/product';
import {CommerceSuccessResponse} from '../../../api/commerce/common/response';
import {QueryCommerceAPIThunkReturn} from '../common/actions';
import {setContext, setView} from '../context/context-actions';
import {
fetchMoreProducts,
fetchProductListing,
Expand Down Expand Up @@ -79,7 +80,9 @@ export const productListingReducer = createReducer(
};

products.splice(currentParentIndex, 1, newParent);
});
})
.addCase(setView, () => getProductListingInitialState())
.addCase(setContext, () => getProductListingInitialState());
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
buildMockProduct,
buildMockBaseProduct,
} from '../../../test/mock-product';
import {setContext, setView} from '../context/context-actions';
import {
executeSearch,
fetchMoreProducts,
Expand Down Expand Up @@ -318,4 +319,48 @@ describe('search-slice', () => {
]);
});
});
it('on #setView, restores the initial state', () => {
state = {
error: {message: 'message', statusCode: 500, type: 'type'},
isLoading: true,
requestId: 'request-id',
facets: [buildMockCommerceRegularFacetResponse()],
products: [
buildMockProduct({ec_name: 'product1'}),
buildMockProduct({ec_name: 'product2'}),
],
queryExecuted: 'query',
responseId: 'response-id',
};

const finalState = commerceSearchReducer(state, setView({url: '/'}));

expect(finalState).toEqual(getCommerceSearchInitialState());
});

it('on #setContext, restores the initial state', () => {
state = {
error: {message: 'message', statusCode: 500, type: 'type'},
isLoading: true,
requestId: 'request-id',
facets: [buildMockCommerceRegularFacetResponse()],
products: [
buildMockProduct({ec_name: 'product1'}),
buildMockProduct({ec_name: 'product2'}),
],
queryExecuted: 'query',
responseId: 'response-id',
};
const finalState = commerceSearchReducer(
state,
setContext({
country: 'CA',
currency: 'CAD',
language: 'fr',
view: {url: '/'},
})
);

expect(finalState).toEqual(getCommerceSearchInitialState());
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
ChildProduct,
} from '../../../api/commerce/common/product';
import {CommerceSuccessResponse} from '../../../api/commerce/common/response';
import {setContext, setView} from '../context/context-actions';
import {
QuerySearchCommerceAPIThunkReturn,
executeSearch,
Expand Down Expand Up @@ -87,7 +88,9 @@ export const commerceSearchReducer = createReducer(
};

products.splice(currentParentIndex, 1, newParent);
});
})
.addCase(setView, () => getCommerceSearchInitialState())
.addCase(setContext, () => getCommerceSearchInitialState());
}
);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {buildMockFacetResponse} from '../../../test/mock-facet-response';
import {buildFetchProductListingResponse} from '../../../test/mock-product-listing';
import {buildMockSearch} from '../../../test/mock-search';
import {buildMockSearchResponse} from '../../../test/mock-search-response';
import {setContext, setView} from '../../commerce/context/context-actions';
import {Parameters} from '../../commerce/parameters/parameters-actions';
import {restoreProductListingParameters} from '../../commerce/product-listing-parameters/product-listing-parameters-actions';
import {fetchProductListing} from '../../commerce/product-listing/product-listing-actions';
Expand Down Expand Up @@ -149,4 +150,21 @@ describe('facet-order slice', () => {
]);
});
});
it('#setView restore the initial state', () => {
const facetIds = ['facetA', 'facetB'];
dispatchMockSearch(facetIds);
expect(state).toEqual(facetIds);

dispatchMock(setView);
expect(state).toEqual(getFacetOrderInitialState());
});

it('#setContext (commerce) restore the initial state', () => {
const facetIds = ['facetA', 'facetB'];
dispatchMockSearch(facetIds);
expect(state).toEqual(facetIds);

dispatchMock(setContext);
expect(state).toEqual(getFacetOrderInitialState());
});
});
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {AnyAction, createReducer} from '@reduxjs/toolkit';
import {setContext, setView} from '../../commerce/context/context-actions';
import {Parameters} from '../../commerce/parameters/parameters-actions';
import {restoreProductListingParameters} from '../../commerce/product-listing-parameters/product-listing-parameters-actions';
import {fetchProductListing} from '../../commerce/product-listing/product-listing-actions';
Expand All @@ -19,7 +20,9 @@ export const facetOrderReducer = createReducer(
.addCase(restoreProductListingParameters, handleRestoreParameters)
.addCase(change.fulfilled, (state, action) => {
return action.payload?.facetOrder ?? state;
});
})
.addCase(setView, () => getFacetOrderInitialState())
.addCase(setContext, () => getFacetOrderInitialState());
}
);

Expand Down
2 changes: 1 addition & 1 deletion packages/samples/headless-commerce-react/src/App.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {render} from '@testing-library/react';
import {vi, MockInstance} from 'vitest';
import App from './App';
import App from './App.js';

let errorSpy: MockInstance<{
(...data: unknown[]): void;
Expand Down
4 changes: 2 additions & 2 deletions packages/samples/headless-commerce-react/src/App.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import {Suspense} from 'react';
import {getEngine} from './context/engine';
import Router from './router/router';
import {getEngine} from './context/engine.js';
import Router from './router/router.js';

export default function App() {
const engine = getEngine();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import {
DateFacetValue,
BreadcrumbManager as HeadlessBreadcrumbManager,
} from '@coveo/headless/commerce';
import {AnyFacetValueResponse} from '@coveo/headless/dist/definitions/features/commerce/facets/facet-set/interfaces/response';
import {useEffect, useState} from 'react';

interface BreadcrumbManagerProps {
Expand All @@ -26,7 +25,11 @@ export default function BreadcrumbManager(props: BreadcrumbManagerProps) {
}

const renderBreadcrumbValue = (
value: AnyFacetValueResponse,
value:
| CategoryFacetValue
| RegularFacetValue
| NumericFacetValue
| DateFacetValue,
type: string
) => {
switch (type) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,13 @@ export default function CartTab(props: ICartTab) {
return (
<span>
<input
type="radio"
aria-label={`Cart (${state.totalQuantity})`}
checked={window.location.pathname === '/cart'}
id="cart"
name="cart"
value="/cart"
checked={window.location.pathname === '/cart'}
onChange={onChange}
type="radio"
value="/cart"
/>
<label htmlFor="cart">
Cart<span>({state.totalQuantity})</span>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import {CartItem, Cart as HeadlessCart} from '@coveo/headless/commerce';
import {useEffect, useState} from 'react';
import {saveCartItemsToLocaleStorage} from '../../utils/cart-utils';
import {formatCurrency} from '../../utils/format-currency';
import {saveCartItemsToLocaleStorage} from '../../utils/cart-utils.js';
import {formatCurrency} from '../../utils/format-currency.js';

interface ICartProps {
controller: HeadlessCart;
Expand Down
Loading

0 comments on commit 5808edb

Please sign in to comment.