diff --git a/src/components/JsonViewer/index.tsx b/src/components/JsonViewer/index.tsx
new file mode 100644
index 00000000..33bb66f9
--- /dev/null
+++ b/src/components/JsonViewer/index.tsx
@@ -0,0 +1 @@
+export * from './JsonViewer';
diff --git a/src/components/__tests__/JsonViewer.tsx b/src/components/__tests__/JsonViewer.tsx
new file mode 100644
index 00000000..33677c0a
--- /dev/null
+++ b/src/components/__tests__/JsonViewer.tsx
@@ -0,0 +1,44 @@
+import * as React from 'react';
+import { mount, ReactWrapper } from 'enzyme';
+
+import { JsonViewer } from '../';
+import { withTheme } from '../testProviders';
+
+import { ClipboardService } from '../../services/ClipboardService';
+
+const origCopySelected = ClipboardService.copySelected;
+
+describe('Components', () => {
+ describe('JsonViewer', () => {
+ let component: ReactWrapper;
+ const data = { a: 1, b: { c: 'hello' } };
+ beforeEach(() => {
+ component = mount(withTheme());
+ ClipboardService.copySelected = origCopySelected;
+ });
+
+ test('should render inner HTML', () => {
+ expect(component.html()).toContain('class="redoc-json"');
+ });
+
+ test('should collapse/uncollapse', () => {
+ expect(component.html()).not.toContain('class="hoverable"'); // all are collapesed by default
+ const expandAll = component.find('div > span[children=" Expand all "]');
+ expandAll.simulate('click');
+ expect(component.html()).toContain('class="hoverable"'); // all are collapesed
+
+ const collapseAll = component.find('div > span[children=" Collapse all "]');
+ collapseAll.simulate('click');
+ expect(component.html()).not.toContain('class="hoverable"'); // all are collapesed
+ });
+
+ test('should collapse/uncollapse', () => {
+ ClipboardService.copySelected = jest.fn();
+
+ const copy = component.find('span[onClick]').first();
+ copy.simulate('click');
+
+ expect(ClipboardService.copySelected as jest.Mock).toHaveBeenCalled();
+ });
+ });
+});
diff --git a/src/components/index.ts b/src/components/index.ts
index 2d1f9347..45a4ead1 100644
--- a/src/components/index.ts
+++ b/src/components/index.ts
@@ -6,6 +6,7 @@ export * from './SearchBox/SearchBox';
export * from './Operation/Operation';
export * from './Loading/Loading';
export * from './RedocStandalone';
+export * from './JsonViewer';
export * from './ErrorBoundary';
export * from './StoreProvider';
diff --git a/src/components/testProviders.tsx b/src/components/testProviders.tsx
new file mode 100644
index 00000000..5ee989d4
--- /dev/null
+++ b/src/components/testProviders.tsx
@@ -0,0 +1,13 @@
+import * as React from 'react';
+import { ThemeProvider } from 'styled-components';
+import defaultTheme from '../theme';
+
+export default class TestThemeProvider extends React.Component {
+ render() {
+ return {this.props.children};
+ }
+}
+
+export function withTheme(children) {
+ return {children};
+}