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

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

Merged
merged 4 commits into from
Jul 1, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading