Skip to content
Open
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
18,019 changes: 9,885 additions & 8,134 deletions package-lock.json

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
{
"name": "@openstax/ui-components",
"version": "1.23.5",
"version": "1.23.7",
"license": "MIT",
"os": [ "darwin", "linux" ],
"repository": "https://github.com/openstax/ui-components.git",
"publishConfig": {
"registry": "https://npm.pkg.github.com"
Expand Down
73 changes: 25 additions & 48 deletions src/components/ErrorBoundary.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,14 +5,16 @@ import * as Sentry from '@sentry/react';
import { findByTestId } from '../test/utils';
import { SessionExpiredError } from '@openstax/ts-utils/errors';

// Sentry v8+ exposes its named exports as read-only getters, so they can no longer
// be replaced with jest.spyOn directly. Mock the module to make lastEventId spyable
// while keeping the real init/captureException so the testkit transport still works.
jest.mock('@sentry/react', () => ({
__esModule: true,
...jest.requireActual('@sentry/react'),
lastEventId: jest.fn(),
}));
// Sentry v8+ exposes named exports as read-only getters; mock init so it is
// spyable while delegating to the real implementation for testkit transport setup.
jest.mock('@sentry/react', () => {
const actual = jest.requireActual('@sentry/react');
return {
__esModule: true,
...actual,
init: jest.fn(actual.init),
};
});

const { testkit, sentryTransport } = sentryTestkit();

Expand All @@ -24,8 +26,6 @@ describe('ErrorBoundary', () => {
dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
transport: sentryTransport,
});

jest.spyOn(Sentry, 'lastEventId').mockReturnValue('someuuid');
});

afterEach(() => {
Expand All @@ -49,7 +49,7 @@ describe('ErrorBoundary', () => {
spy.mockImplementation(() => undefined);

const render = renderer.create(
<ErrorBoundary renderFallback>
<ErrorBoundary>
<ErrorComponent />
</ErrorBoundary>
);
Expand All @@ -60,30 +60,6 @@ describe('ErrorBoundary', () => {
spy.mockRestore();
});

it('resets error', () => {
const spy = jest.spyOn(console, 'error')
spy.mockImplementation(() => undefined);

let render: ReactTestRenderer;
expect(() => {
render = renderer.create(
<ErrorBoundary
renderFallback
fallback={({ resetError }: {
resetError: () => void
}) => { resetError(); return <></> }}
>
<ErrorComponent />
</ErrorBoundary>
);
}).toThrow();

expect(() => findByTestId(render.root, 'error-fallback')).toThrow();
expect(testkit.reports()).toHaveLength(1);

spy.mockRestore();
});

it('sets level appropriately', () => {
const spy = jest.spyOn(console, 'error');
spy.mockImplementation(() => undefined);
Expand All @@ -97,14 +73,14 @@ describe('ErrorBoundary', () => {

// Should create warning (reports[0])
renderer.create(
<ErrorBoundary renderFallback>
<ErrorBoundary>
<SessionExpiredComponent />
</ErrorBoundary>
);

// Should create error (reports[1])
renderer.create(
<ErrorBoundary renderFallback>
<ErrorBoundary>
<ErrorComponent />
</ErrorBoundary>
);
Expand Down Expand Up @@ -132,7 +108,6 @@ describe('ErrorBoundary', () => {
// Should create debug (reports[0])
renderer.create(
<ErrorBoundary
renderFallback
errorLevels={{ SessionExpiredError: 'debug' }}
>
<SessionExpiredComponent />
Expand All @@ -141,7 +116,7 @@ describe('ErrorBoundary', () => {

// Should create error (reports[1])
renderer.create(
<ErrorBoundary renderFallback>
<ErrorBoundary>
<ErrorComponent />
</ErrorBoundary>
);
Expand All @@ -158,7 +133,6 @@ describe('ErrorBoundary', () => {

renderer.create(
<ErrorBoundary
renderFallback
errorLevels={{ SessionExpiredError: unsetLevel }}
>
<SessionExpiredComponent />
Expand All @@ -181,7 +155,6 @@ describe('ErrorBoundary', () => {

const tree = renderer.create(
<ErrorBoundary
renderFallback
errorFallbacks={{
'SessionExpiredError': <>You are signed out</>,
}}
Expand All @@ -196,19 +169,21 @@ describe('ErrorBoundary', () => {
});

it('inits Sentry', () => {
const initSpy = jest.spyOn(Sentry, 'init');
const initMock = Sentry.init as jest.Mock;
initMock.mockClear();

act(() => {
renderer.create(
<ErrorBoundary sentryDsn='https://examplePublicKey@o0.ingest.sentry.io/0' />
);
});

expect(initSpy).toHaveBeenCalled();
expect(initMock).toHaveBeenCalled();
});

it('can override Sentry init', () => {
const initSpy = jest.spyOn(Sentry, 'init');
const initMock = Sentry.init as jest.Mock;
initMock.mockClear();
const config = {
dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
enabled: false,
Expand All @@ -223,10 +198,12 @@ describe('ErrorBoundary', () => {
);
});

expect(initSpy).toHaveBeenCalledWith(config);
expect(initMock).toHaveBeenCalledWith(config);
});

it('is idempotent when the init effect re-runs (does not throw or re-init)', () => {
const initSpy = jest.spyOn(Sentry, 'init');
const initMock = Sentry.init as jest.Mock;
initMock.mockClear();
const config = {
dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0',
enabled: false,
Expand All @@ -242,7 +219,7 @@ describe('ErrorBoundary', () => {
);
});

expect(initSpy).toHaveBeenCalledTimes(1);
expect(initMock).toHaveBeenCalledTimes(1);

let caught;
const saveError = console.error;
Expand All @@ -255,6 +232,6 @@ describe('ErrorBoundary', () => {
console.error = saveError;

expect(caught).toBeUndefined();
expect(initSpy).toHaveBeenCalledTimes(1);
expect(initMock).toHaveBeenCalledTimes(1);
});
});
33 changes: 7 additions & 26 deletions src/components/ErrorBoundary.stories.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,24 +32,10 @@ const AsyncTriggerErrorDisplayButton = () => {
</button>;
};

export const InlineMessages = () => {
const [showError, setShowError] = React.useState(false);

return <ErrorBoundary>
<ErrorMessage />
<ErrorComponent
doThrow={showError}
setShowError={setShowError}
/>
<button onClick={() => { setShowError(true) }}>Throw Error</button>
<AsyncTriggerErrorDisplayButton />
</ErrorBoundary>
};

export const Fallback_GenericError_Default = () => {
const [showError, setShowError] = React.useState(false);

return <ErrorBoundary renderFallback sentryDsn="https://0@o0.ingest.sentry.io/0">
return <ErrorBoundary sentryDsn="https://0@o0.ingest.sentry.io/0">
<ErrorComponent
doThrow={showError}
setShowError={setShowError}
Expand All @@ -62,15 +48,12 @@ export const Fallback_GenericError_Default = () => {
export const Fallback_GenericError_Custom = () => {
const [showError, setShowError] = React.useState(false);

return <ErrorBoundary
renderFallback
fallback={(props) => (
<>
return <ErrorBoundary errorFallbacks={{
'Error': <>
<h2>This is a custom error fallback</h2>
<p>{props && String(props.error)}</p>
{props && props.resetError ? <button onClick={props && props.resetError}>Reset</button> : null}
</>
)}>
<ErrorMessage message="some message huh" />
</>
}}>
<ErrorComponent
doThrow={showError}
setShowError={setShowError}
Expand Down Expand Up @@ -99,9 +82,7 @@ export const Fallback_SpecialError = () => {
const [showError1, setShowError1] = React.useState(false);
const [showError2, setShowError2] = React.useState(false);

return <ErrorBoundary
renderFallback
>
return <ErrorBoundary>
<ErrorComponent
doThrow={showError1}
setShowError={setShowError1}
Expand Down
Loading
Loading