Skip to content

Commit

Permalink
[sitecore-jss-react] [ErrorBoundaries] Added unit tests to cover with…
Browse files Browse the repository at this point in the history
…Placeholder HOC (#1829)

* [sitecore-jss-react] [ErrorBoundaries] Added unit tests to cover withPlaceholder HOC

* Updated CHANGELOG

* Removed unused import
  • Loading branch information
illiakovalenko authored Jul 1, 2024
1 parent 4a047aa commit 0df6867
Show file tree
Hide file tree
Showing 4 changed files with 139 additions and 296 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ Our versioning strategy is as follows:

### 🎉 New Features & Improvements

* `[sitecore-jss-react]` Introduce ErrorBoundary component. All rendered components are wrapped with it and it will catch client or server side errors from any of its children, display appropriate message and prevent the rest of the application from failing. It accepts and can display custom error component and loading message if it is passed as a prop to parent Placeholder. ([#1786](https://github.com/Sitecore/jss/pull/1786))([#1790](https://github.com/Sitecore/jss/pull/1790))([#1793](https://github.com/Sitecore/jss/pull/1793))([#1794](https://github.com/Sitecore/jss/pull/1794))([#1799](https://github.com/Sitecore/jss/pull/1799))([#1807](https://github.com/Sitecore/jss/pull/1807))
* `[sitecore-jss-react]` Introduce ErrorBoundary component. All rendered components are wrapped with it and it will catch client or server side errors from any of its children, display appropriate message and prevent the rest of the application from failing. It accepts and can display custom error component and loading message if it is passed as a prop to parent Placeholder. ([#1786](https://github.com/Sitecore/jss/pull/1786))([#1790](https://github.com/Sitecore/jss/pull/1790))([#1793](https://github.com/Sitecore/jss/pull/1793))([#1794](https://github.com/Sitecore/jss/pull/1794))([#1799](https://github.com/Sitecore/jss/pull/1799))([#1807](https://github.com/Sitecore/jss/pull/1807))([#1829](https://github.com/Sitecore/jss/pull/1829))
* `[sitecore-jss-nextjs]` Enforce CORS policy that matches Sitecore Pages domains for editing middleware API endpoints ([#1798](https://github.com/Sitecore/jss/pull/1798)[#1801](https://github.com/Sitecore/jss/pull/1801))
* `[sitecore-jss]` _GraphQLRequestClient_ now can accept custom 'headers' in the constructor or via _createClientFactory_ ([#1806](https://github.com/Sitecore/jss/pull/1806))
* `[templates/nextjs]` Removed cors header for API endpoints from _lib/next-config/plugins/cors-header_ plugin since cors is handled by API handlers / middlewares ([#1806](https://github.com/Sitecore/jss/pull/1806))
Expand Down
141 changes: 107 additions & 34 deletions packages/sitecore-jss-react/src/enhancers/withPlaceholder.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,6 @@ import React, { ReactElement, ReactNode } from 'react';
import { expect } from 'chai';
import { mount } from 'enzyme';
import { convertedDevData as nonEeDevData } from '../test-data/non-ee-data';
import { convertedDevDataWithoutParams as nonEeDevDataWithoutParams } from '../test-data/non-ee-data';
import { convertedDataWithoutParams as eeDataWithoutParams } from '../test-data/ee-data';
import { convertedData as eeData } from '../test-data/ee-data';
import * as metadataData from '../test-data/metadata-data';
import { withPlaceholder } from '../enhancers/withPlaceholder';
Expand All @@ -17,7 +15,6 @@ import PropTypes from 'prop-types';
import { ComponentFactory } from '../components/sharedTypes';
import {
ComponentRendering,
EditMode,
LayoutServiceData,
RouteData,
} from '@sitecore-jss/sitecore-jss/layout';
Expand Down Expand Up @@ -66,11 +63,28 @@ const ErrorMessageComponent: React.FC = () => (
<div className="error-handled">Your error has been... dealt with.</div>
);

const delay = (timeout, promise?) => {
return new Promise((resolve) => {
setTimeout(resolve, timeout);
}).then(() => promise);
};

const componentFactory: ComponentFactory = (componentName: string) => {
const components = new Map<string, React.FC<any>>();

components.set('DownloadCallout', DownloadCallout);
components.set('Jumbotron', () => <div className="jumbotron-mock"></div>);
components.set('BrokenComponent', () => {
throw new Error('BrokenComponent error');
});
components.set(
'DynamicComponent',
React.lazy(() =>
delay(500, () => {
throw new Error('DynamicComponent error');
})
)
);

return components.get(componentName) || null;
};
Expand All @@ -80,13 +94,13 @@ const testData = [
{ label: 'LayoutService data - EE on', data: eeData },
];

const testDataWithoutParams = [
{ label: 'Dev data without params', data: nonEeDevDataWithoutParams },
{ label: 'LayoutService data - EE on without params', data: eeDataWithoutParams },
];

describe('withPlaceholder HOC', () => {
describe('Error handling', () => {
before(() => {
// Set to development mode to show error details
process.env.NODE_ENV = 'development';
});

it('should render default error component on wrapped component error', () => {
const phKey = 'page-content';
const props: EnhancedOmit<PlaceholderProps, 'sitecoreContext'> = {
Expand Down Expand Up @@ -123,12 +137,94 @@ describe('withPlaceholder HOC', () => {
);
expect(renderedComponent.find('.error-handled').length).to.equal(1);
});

it('should render nested broken component', () => {
const component = (nonEeDevData.sitecore.route?.placeholders.main as (
| ComponentRendering
| RouteData
)[]).find((c) => (c as ComponentRendering).componentName) as ComponentRendering;
const phKey = 'page-content';
const props: EnhancedOmit<PlaceholderProps, 'sitecoreContext'> = {
name: phKey,
rendering: component,
};
const Element = withPlaceholder(phKey)(Home);
const renderedComponent = mount(
<SitecoreContext layoutData={nonEeDevData} componentFactory={componentFactory}>
<Element {...props} />
</SitecoreContext>
);

expect(renderedComponent.find('.download-callout-mock').length).to.equal(1);
expect(renderedComponent.find('.sc-jss-placeholder-error').length).to.equal(1);
expect(renderedComponent.find('h4').length).to.equal(1);
expect(renderedComponent.find('h4').html()).to.equal('<h4>Loading component...</h4>');
});

it('should render nested components using custom error component', () => {
const component = (nonEeDevData.sitecore.route?.placeholders.main as (
| ComponentRendering
| RouteData
)[]).find((c) => (c as ComponentRendering).componentName) as ComponentRendering;
const phKey = 'page-content';
const props: EnhancedOmit<PlaceholderProps, 'sitecoreContext'> = {
name: phKey,
rendering: component,
errorComponent: ErrorMessageComponent,
componentLoadingMessage: 'Custom loading message...',
};
const Element = withPlaceholder(phKey)(Home);
const renderedComponent = mount(
<SitecoreContext layoutData={nonEeDevData} componentFactory={componentFactory}>
<Element {...props} />
</SitecoreContext>
);

expect(renderedComponent.find('.download-callout-mock').length).to.equal(1);
expect(renderedComponent.find('.error-handled').length).to.equal(1);
expect(renderedComponent.find('h4').length).to.equal(1);
expect(renderedComponent.find('h4').html()).to.equal('<h4>Custom loading message...</h4>');
});

describe('Edit mode', () => {
const component = (eeData.sitecore.route?.placeholders.main as (
| ComponentRendering
| RouteData
)[]).find((c) => (c as ComponentRendering).componentName) as ComponentRendering;
const phKey = 'page-content';
const props: EnhancedOmit<PlaceholderProps, 'sitecoreContext'> = {
name: phKey,
rendering: component,
};
const Element = withPlaceholder(phKey)(Home);
const renderedComponent = mount(
<SitecoreContext
layoutData={eeData as LayoutServiceData}
componentFactory={componentFactory}
>
<Element {...props} />
</SitecoreContext>
);

it('should render normal component', () => {
expect(renderedComponent.find('.download-callout-mock').length).to.equal(1);
});

it('should render nested broken component', () => {
expect(renderedComponent.find('.sc-jss-placeholder-error').length).to.equal(1);
});

it('should render nested dynamic broken component', () => {
expect(renderedComponent.find('h4').length).to.equal(1);
expect(renderedComponent.find('h4').html()).to.equal('<h4>Loading component...</h4>');
});
});
});

testData.forEach((dataSet) => {
describe(`with ${dataSet.label}`, () => {
it('should render a placeholder with given key', () => {
const component = (dataSet.data.sitecore.route.placeholders.main as (
const component = (dataSet.data.sitecore.route?.placeholders.main as (
| ComponentRendering
| RouteData
)[]).find((c) => (c as ComponentRendering).componentName) as ComponentRendering;
Expand All @@ -150,7 +246,7 @@ describe('withPlaceholder HOC', () => {
});

it('should render a placeholder with given key and prop', () => {
const component = (dataSet.data.sitecore.route.placeholders.main as (
const component = (dataSet.data.sitecore.route?.placeholders.main as (
| ComponentRendering
| RouteData
)[]).find((c) => (c as ComponentRendering).componentName) as ComponentRendering;
Expand All @@ -176,7 +272,7 @@ describe('withPlaceholder HOC', () => {
});

it('should use propsTransformer method when provided', () => {
const component = (dataSet.data.sitecore.route.placeholders.main as (
const component = (dataSet.data.sitecore.route?.placeholders.main as (
| ComponentRendering
| RouteData
)[]).find((c) => (c as ComponentRendering).componentName) as ComponentRendering;
Expand Down Expand Up @@ -208,29 +304,6 @@ describe('withPlaceholder HOC', () => {
});
});

testDataWithoutParams.forEach((dataSet) => {
describe(`with ${dataSet.label}`, () => {
it('should render a placeholder with given key when params are not passed', () => {
const component = (dataSet.data.sitecore.route.placeholders.main as (
| ComponentRendering
| RouteData
)[]).find((c) => (c as ComponentRendering).componentName) as ComponentRendering;
const phKey = 'page-content';
const props: EnhancedOmit<PlaceholderProps, 'sitecoreContext'> = {
name: phKey,
rendering: component,
};
const Element = withPlaceholder(phKey)(Home);
const renderedComponent = mount(
<SitecoreContext componentFactory={componentFactory}>
<Element {...props} />
</SitecoreContext>
);
expect(renderedComponent.find('.download-callout-mock').length).to.equal(1);
});
});
});

describe('Metadata Mode', () => {
const {
layoutData,
Expand Down
Loading

0 comments on commit 0df6867

Please sign in to comment.