datajunction-ui 0.0.1-rc.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (71) hide show
  1. package/.env +1 -0
  2. package/.env.local +4 -0
  3. package/.env.production +1 -0
  4. package/.eslintrc.js +20 -0
  5. package/.gitattributes +201 -0
  6. package/.github/pull_request_template.md +11 -0
  7. package/.github/workflows/ci.yml +33 -0
  8. package/.husky/pre-commit +6 -0
  9. package/.nvmrc +1 -0
  10. package/.prettierignore +4 -0
  11. package/.prettierrc +9 -0
  12. package/.stylelintrc +7 -0
  13. package/.vscode/extensions.json +8 -0
  14. package/.vscode/launch.json +15 -0
  15. package/.vscode/settings.json +25 -0
  16. package/Dockerfile +6 -0
  17. package/LICENSE +22 -0
  18. package/README.md +10 -0
  19. package/internals/testing/loadable.mock.tsx +6 -0
  20. package/package.json +150 -0
  21. package/public/favicon.ico +0 -0
  22. package/public/index.html +29 -0
  23. package/public/manifest.json +15 -0
  24. package/public/robots.txt +3 -0
  25. package/src/app/__tests__/__snapshots__/index.test.tsx.snap +45 -0
  26. package/src/app/__tests__/index.test.tsx +14 -0
  27. package/src/app/components/ListGroupItem.jsx +17 -0
  28. package/src/app/components/NamespaceHeader.jsx +40 -0
  29. package/src/app/components/Tab.jsx +26 -0
  30. package/src/app/components/__tests__/ListGroupItem.test.tsx +16 -0
  31. package/src/app/components/__tests__/NamespaceHeader.test.jsx +14 -0
  32. package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +26 -0
  33. package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +63 -0
  34. package/src/app/components/djgraph/DJNode.jsx +111 -0
  35. package/src/app/components/djgraph/__tests__/DJNode.test.tsx +24 -0
  36. package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +73 -0
  37. package/src/app/index.tsx +53 -0
  38. package/src/app/pages/ListNamespacesPage/Loadable.jsx +23 -0
  39. package/src/app/pages/ListNamespacesPage/index.jsx +53 -0
  40. package/src/app/pages/NamespacePage/Loadable.jsx +23 -0
  41. package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +45 -0
  42. package/src/app/pages/NamespacePage/__tests__/index.test.tsx +14 -0
  43. package/src/app/pages/NamespacePage/index.jsx +93 -0
  44. package/src/app/pages/NodePage/Loadable.jsx +23 -0
  45. package/src/app/pages/NodePage/NodeColumnTab.jsx +44 -0
  46. package/src/app/pages/NodePage/NodeGraphTab.jsx +160 -0
  47. package/src/app/pages/NodePage/NodeInfoTab.jsx +87 -0
  48. package/src/app/pages/NodePage/NodeStatus.jsx +34 -0
  49. package/src/app/pages/NodePage/index.jsx +100 -0
  50. package/src/app/pages/NotFoundPage/Loadable.tsx +14 -0
  51. package/src/app/pages/NotFoundPage/P.ts +8 -0
  52. package/src/app/pages/NotFoundPage/__tests__/__snapshots__/index.test.tsx.snap +61 -0
  53. package/src/app/pages/NotFoundPage/__tests__/index.test.tsx +21 -0
  54. package/src/app/pages/NotFoundPage/index.tsx +45 -0
  55. package/src/app/pages/Root/Loadable.tsx +23 -0
  56. package/src/app/pages/Root/assets/dj-logo.png +0 -0
  57. package/src/app/pages/Root/index.tsx +42 -0
  58. package/src/app/services/DJService.js +124 -0
  59. package/src/index.tsx +47 -0
  60. package/src/react-app-env.d.ts +4 -0
  61. package/src/reportWebVitals.ts +15 -0
  62. package/src/setupTests.ts +8 -0
  63. package/src/styles/dag-styles.ts +117 -0
  64. package/src/styles/global-styles.ts +588 -0
  65. package/src/styles/index.css +546 -0
  66. package/src/utils/__tests__/__snapshots__/loadable.test.tsx.snap +17 -0
  67. package/src/utils/__tests__/loadable.test.tsx +53 -0
  68. package/src/utils/__tests__/request.test.ts +82 -0
  69. package/src/utils/loadable.tsx +30 -0
  70. package/src/utils/request.ts +54 -0
  71. package/tsconfig.json +31 -0
@@ -0,0 +1,17 @@
1
+ import { Component } from 'react';
2
+
3
+ export default class ListGroupItem extends Component {
4
+ render() {
5
+ const { label, value } = this.props;
6
+ return (
7
+ <div className="list-group-item d-flex">
8
+ <div className="d-flex gap-2 w-100 justify-content-between py-3">
9
+ <div>
10
+ <h6 className="mb-0 w-100">{label}</h6>
11
+ <p className="mb-0 opacity-75">{value}</p>
12
+ </div>
13
+ </div>
14
+ </div>
15
+ );
16
+ }
17
+ }
@@ -0,0 +1,40 @@
1
+ import { Component } from 'react';
2
+
3
+ export default class NamespaceHeader extends Component {
4
+ render() {
5
+ const { namespace } = this.props;
6
+ const namespaceParts = namespace.split('.');
7
+ const namespaceList = namespaceParts.map((piece, index) => {
8
+ return (
9
+ <li className="breadcrumb-item">
10
+ <a
11
+ className="link-body-emphasis"
12
+ href={'/namespaces/' + namespaceParts.slice(0, index + 1).join('.')}
13
+ >
14
+ {piece}
15
+ </a>
16
+ </li>
17
+ );
18
+ });
19
+ return (
20
+ <ol className="breadcrumb breadcrumb-chevron p-3 bg-body-tertiary rounded-3">
21
+ <li className="breadcrumb-item">
22
+ <a href="/namespaces/">
23
+ <svg
24
+ xmlns="http://www.w3.org/2000/svg"
25
+ width="16"
26
+ height="16"
27
+ fill="currentColor"
28
+ className="bi bi-house-door-fill"
29
+ viewBox="0 0 16 16"
30
+ style={{ paddingBottom: '0.2rem' }}
31
+ >
32
+ <path d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z" />
33
+ </svg>
34
+ </a>
35
+ </li>
36
+ {namespaceList}
37
+ </ol>
38
+ );
39
+ }
40
+ }
@@ -0,0 +1,26 @@
1
+ import { Component } from 'react';
2
+
3
+ export default class Tab extends Component {
4
+ render() {
5
+ const { id, onClick, selectedTab } = this.props;
6
+ return (
7
+ <div className={selectedTab === id ? 'col active' : 'col'}>
8
+ <div className="header-tabs nav-overflow nav nav-tabs">
9
+ <div className="nav-item">
10
+ <a
11
+ id={id}
12
+ role="button"
13
+ className="nav-link"
14
+ tabIndex="0"
15
+ href="#/"
16
+ onClick={onClick}
17
+ >
18
+ {this.props.name}
19
+ {/*<span className="rounded-pill badge bg-secondary-soft">823</span>*/}
20
+ </a>
21
+ </div>
22
+ </div>
23
+ </div>
24
+ );
25
+ }
26
+ }
@@ -0,0 +1,16 @@
1
+ import * as React from 'react';
2
+ import { createRenderer } from 'react-test-renderer/shallow';
3
+
4
+ import ListGroupItem from '../ListGroupItem';
5
+
6
+ const renderer = createRenderer();
7
+
8
+ describe('<ListGroupItem />', () => {
9
+ it('should render and match the snapshot', () => {
10
+ renderer.render(
11
+ <ListGroupItem label="Name" value={<span>Something</span>} />,
12
+ );
13
+ const renderedOutput = renderer.getRenderOutput();
14
+ expect(renderedOutput).toMatchSnapshot();
15
+ });
16
+ });
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+ import { createRenderer } from 'react-test-renderer/shallow';
3
+
4
+ import NamespaceHeader from '../NamespaceHeader';
5
+
6
+ const renderer = createRenderer();
7
+
8
+ describe('<NamespaceHeader />', () => {
9
+ it('should render and match the snapshot', () => {
10
+ renderer.render(<NamespaceHeader namespace="shared.dimensions.accounts" />);
11
+ const renderedOutput = renderer.getRenderOutput();
12
+ expect(renderedOutput).toMatchSnapshot();
13
+ });
14
+ });
@@ -0,0 +1,26 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<ListGroupItem /> should render and match the snapshot 1`] = `
4
+ <div
5
+ className="list-group-item d-flex"
6
+ >
7
+ <div
8
+ className="d-flex gap-2 w-100 justify-content-between py-3"
9
+ >
10
+ <div>
11
+ <h6
12
+ className="mb-0 w-100"
13
+ >
14
+ Name
15
+ </h6>
16
+ <p
17
+ className="mb-0 opacity-75"
18
+ >
19
+ <span>
20
+ Something
21
+ </span>
22
+ </p>
23
+ </div>
24
+ </div>
25
+ </div>
26
+ `;
@@ -0,0 +1,63 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<NamespaceHeader /> should render and match the snapshot 1`] = `
4
+ <ol
5
+ className="breadcrumb breadcrumb-chevron p-3 bg-body-tertiary rounded-3"
6
+ >
7
+ <li
8
+ className="breadcrumb-item"
9
+ >
10
+ <a
11
+ href="/namespaces/"
12
+ >
13
+ <svg
14
+ className="bi bi-house-door-fill"
15
+ fill="currentColor"
16
+ height="16"
17
+ style={
18
+ Object {
19
+ "paddingBottom": "0.2rem",
20
+ }
21
+ }
22
+ viewBox="0 0 16 16"
23
+ width="16"
24
+ xmlns="http://www.w3.org/2000/svg"
25
+ >
26
+ <path
27
+ d="M6.5 14.5v-3.505c0-.245.25-.495.5-.495h2c.25 0 .5.25.5.5v3.5a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5v-7a.5.5 0 0 0-.146-.354L13 5.793V2.5a.5.5 0 0 0-.5-.5h-1a.5.5 0 0 0-.5.5v1.293L8.354 1.146a.5.5 0 0 0-.708 0l-6 6A.5.5 0 0 0 1.5 7.5v7a.5.5 0 0 0 .5.5h4a.5.5 0 0 0 .5-.5Z"
28
+ />
29
+ </svg>
30
+ </a>
31
+ </li>
32
+ <li
33
+ className="breadcrumb-item"
34
+ >
35
+ <a
36
+ className="link-body-emphasis"
37
+ href="/namespaces/shared"
38
+ >
39
+ shared
40
+ </a>
41
+ </li>
42
+ <li
43
+ className="breadcrumb-item"
44
+ >
45
+ <a
46
+ className="link-body-emphasis"
47
+ href="/namespaces/shared.dimensions"
48
+ >
49
+ dimensions
50
+ </a>
51
+ </li>
52
+ <li
53
+ className="breadcrumb-item"
54
+ >
55
+ <a
56
+ className="link-body-emphasis"
57
+ href="/namespaces/shared.dimensions.accounts"
58
+ >
59
+ accounts
60
+ </a>
61
+ </li>
62
+ </ol>
63
+ `;
@@ -0,0 +1,111 @@
1
+ import React, { memo } from 'react';
2
+ import { Handle, Position } from 'reactflow';
3
+
4
+ function renderBasedOnDJNodeType(param) {
5
+ switch (param) {
6
+ case 'source':
7
+ return { backgroundColor: '#7EB46150', color: '#7EB461' };
8
+ case 'transform':
9
+ return { backgroundColor: '#6DAAA750', color: '#6DAAA7' };
10
+ case 'dimension':
11
+ return { backgroundColor: '#CF7D2950', color: '#CF7D29' };
12
+ case 'metric':
13
+ return { backgroundColor: '#A27E8650', color: '#A27E86' };
14
+ case 'cube':
15
+ return { backgroundColor: '#C2180750', color: '#C21807' };
16
+ default:
17
+ return {};
18
+ }
19
+ }
20
+
21
+ function capitalize(string) {
22
+ return string.charAt(0).toUpperCase() + string.slice(1);
23
+ }
24
+
25
+ const Collapse = ({ collapsed, text, children }) => {
26
+ const [isCollapsed, setIsCollapsed] = React.useState(collapsed);
27
+
28
+ return (
29
+ <>
30
+ <div className="collapse">
31
+ <button
32
+ className="collapse-button"
33
+ onClick={() => setIsCollapsed(!isCollapsed)}
34
+ >
35
+ {isCollapsed ? '\u25B6 Show' : '\u25BC Hide'} {text}
36
+ </button>
37
+ <div
38
+ className={`collapse-content ${
39
+ isCollapsed ? 'collapsed' : 'expanded'
40
+ }`}
41
+ aria-expanded={isCollapsed}
42
+ >
43
+ {children}
44
+ </div>
45
+ </div>
46
+ </>
47
+ );
48
+ };
49
+
50
+ export function DJNode({ id, data }) {
51
+ const columnsRenderer = data =>
52
+ data.column_names.map(col => (
53
+ <tr>
54
+ <td>
55
+ {data.primary_key.includes(col.name) ? (
56
+ <b>{col.name} (PK)</b>
57
+ ) : (
58
+ <>{col.name}</>
59
+ )}
60
+ </td>
61
+ <td style={{ textAlign: 'right' }}>{col.type}</td>
62
+ </tr>
63
+ ));
64
+ // const dimensionsRenderer = data =>
65
+ // data.dimensions.map(dim => (
66
+ // <tr>
67
+ // <td>{dim}</td>
68
+ // </tr>
69
+ // ));
70
+
71
+ return (
72
+ <>
73
+ <Handle
74
+ type="target"
75
+ position={Position.Left}
76
+ style={{ backgroundColor: '#ccc' }}
77
+ />
78
+ <div className="dj-node__full" style={renderBasedOnDJNodeType(data.type)}>
79
+ <div className="dj-node__header">
80
+ <div className="serif">
81
+ {data.name
82
+ .split('.')
83
+ .slice(0, data.name.split('.').length - 1)
84
+ .join(' \u25B6 ')}
85
+ </div>
86
+ </div>
87
+ <div className="dj-node__body">
88
+ <b>{capitalize(data.type)}</b>:{' '}
89
+ {data.type === 'source' ? data.table : data.display_name}
90
+ <Collapse
91
+ collapsed={true}
92
+ text={data.type !== 'metric' ? 'columns' : 'dimensions'}
93
+ >
94
+ <div className="dj-node__metadata">
95
+ {
96
+ data.type !== 'metric' ? columnsRenderer(data) : '' // dimensionsRenderer(data)
97
+ }
98
+ </div>
99
+ </Collapse>
100
+ </div>
101
+ </div>
102
+ <Handle
103
+ type="source"
104
+ position={Position.Right}
105
+ style={{ backgroundColor: '#ccc' }}
106
+ />
107
+ </>
108
+ );
109
+ }
110
+
111
+ export default memo(DJNode);
@@ -0,0 +1,24 @@
1
+ import * as React from 'react';
2
+ import { createRenderer } from 'react-test-renderer/shallow';
3
+
4
+ import { DJNode } from '../DJNode';
5
+
6
+ const renderer = createRenderer();
7
+
8
+ describe('<DJNode />', () => {
9
+ it('should render and match the snapshot', () => {
10
+ renderer.render(
11
+ <DJNode
12
+ id="1"
13
+ data={{
14
+ name: 'shared.dimensions.accounts',
15
+ column_names: ['a'],
16
+ type: 'source',
17
+ primary_key: ['id'],
18
+ }}
19
+ />,
20
+ );
21
+ const renderedOutput = renderer.getRenderOutput();
22
+ expect(renderedOutput).toMatchSnapshot();
23
+ });
24
+ });
@@ -0,0 +1,73 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<DJNode /> should render and match the snapshot 1`] = `
4
+ <React.Fragment>
5
+ <Memo(Handle)
6
+ position="left"
7
+ style={
8
+ Object {
9
+ "backgroundColor": "#ccc",
10
+ }
11
+ }
12
+ type="target"
13
+ />
14
+ <div
15
+ className="dj-node__full"
16
+ style={
17
+ Object {
18
+ "backgroundColor": "#7EB46150",
19
+ "color": "#7EB461",
20
+ }
21
+ }
22
+ >
23
+ <div
24
+ className="dj-node__header"
25
+ >
26
+ <div
27
+ className="serif"
28
+ >
29
+ shared ▶ dimensions
30
+ </div>
31
+ </div>
32
+ <div
33
+ className="dj-node__body"
34
+ >
35
+ <b>
36
+ Source
37
+ </b>
38
+ :
39
+
40
+ <Collapse
41
+ collapsed={true}
42
+ text="columns"
43
+ >
44
+ <div
45
+ className="dj-node__metadata"
46
+ >
47
+ <tr>
48
+ <td>
49
+ <React.Fragment />
50
+ </td>
51
+ <td
52
+ style={
53
+ Object {
54
+ "textAlign": "right",
55
+ }
56
+ }
57
+ />
58
+ </tr>
59
+ </div>
60
+ </Collapse>
61
+ </div>
62
+ </div>
63
+ <Memo(Handle)
64
+ position="right"
65
+ style={
66
+ Object {
67
+ "backgroundColor": "#ccc",
68
+ }
69
+ }
70
+ type="source"
71
+ />
72
+ </React.Fragment>
73
+ `;
@@ -0,0 +1,53 @@
1
+ /**
2
+ * This component is the skeleton around the actual pages, and only contains
3
+ * components that should be seen on all pages, like the logo or navigation bar.
4
+ */
5
+
6
+ import * as React from 'react';
7
+ import { Helmet } from 'react-helmet-async';
8
+ import { BrowserRouter, Routes, Route } from 'react-router-dom';
9
+
10
+ import { GlobalStyle } from 'styles/global-styles';
11
+ import { DAGStyle } from '../styles/dag-styles';
12
+
13
+ import { NamespacePage } from './pages/NamespacePage/Loadable';
14
+ import { NodePage } from './pages/NodePage/Loadable';
15
+ import { NotFoundPage } from './pages/NotFoundPage/Loadable';
16
+ import { Root } from './pages/Root/Loadable';
17
+ import { ListNamespacesPage } from './pages/ListNamespacesPage';
18
+
19
+ export function App() {
20
+ return (
21
+ <BrowserRouter>
22
+ <Helmet
23
+ titleTemplate="DataJunction: %s"
24
+ defaultTitle="DataJunction: A Metrics Platform"
25
+ >
26
+ <meta
27
+ name="description"
28
+ content="DataJunction serves as a semantic layer to help manage metrics"
29
+ />
30
+ </Helmet>
31
+
32
+ <Routes>
33
+ <Route
34
+ path="/"
35
+ element={<Root />}
36
+ children={
37
+ <>
38
+ <Route path="/nodes/:name" element={<NodePage />} />
39
+ <Route
40
+ path="/namespaces/:namespace"
41
+ element={<NamespacePage />}
42
+ />
43
+ <Route path="/namespaces/" element={<ListNamespacesPage />} />
44
+ </>
45
+ }
46
+ />
47
+ <Route path="*" element={<NotFoundPage />} />
48
+ </Routes>
49
+ <GlobalStyle />
50
+ <DAGStyle />
51
+ </BrowserRouter>
52
+ );
53
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Asynchronously loads the component for namespaces node-viewing page
3
+ */
4
+
5
+ import * as React from 'react';
6
+ import { lazyLoad } from 'utils/loadable';
7
+ import styled from 'styled-components/macro';
8
+
9
+ const LoadingWrapper = styled.div`
10
+ width: 100%;
11
+ height: 100vh;
12
+ display: flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+ `;
16
+
17
+ export const ListNamespacesPage = lazyLoad(
18
+ () => import('./index'),
19
+ module => module.ListNamespacesPage,
20
+ {
21
+ fallback: <LoadingWrapper></LoadingWrapper>,
22
+ },
23
+ );
@@ -0,0 +1,53 @@
1
+ import * as React from 'react';
2
+ import { useEffect, useState } from 'react';
3
+ import NamespaceHeader from '../../components/NamespaceHeader';
4
+ const datajunction = require('datajunction');
5
+ const dj = new datajunction.DJClient('http://localhost:8000');
6
+
7
+ export function ListNamespacesPage() {
8
+ const [state, setState] = useState({
9
+ namespaces: [],
10
+ });
11
+
12
+ useEffect(() => {
13
+ const fetchData = async () => {
14
+ const namespaces = await dj.namespaces.get();
15
+ setState({
16
+ namespaces: namespaces,
17
+ });
18
+ };
19
+ fetchData().catch(console.error);
20
+ }, []);
21
+
22
+ const namespacesList = state.namespaces.map(node => (
23
+ <tr>
24
+ <td>
25
+ <a href={'/namespaces/' + node.namespace}>{node.namespace}</a>
26
+ </td>
27
+ <td>5</td>
28
+ </tr>
29
+ ));
30
+
31
+ // @ts-ignore
32
+ return (
33
+ <>
34
+ <div className="mid">
35
+ <NamespaceHeader namespace="" />
36
+ <div className="card">
37
+ <div className="card-header">
38
+ <h2>Namespaces</h2>
39
+ <div className="table-responsive">
40
+ <table className="card-table table">
41
+ <thead>
42
+ <th>Namespace</th>
43
+ <th>Node Count</th>
44
+ </thead>
45
+ {namespacesList}
46
+ </table>
47
+ </div>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ </>
52
+ );
53
+ }
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Asynchronously loads the component for namespaces node-viewing page
3
+ */
4
+
5
+ import * as React from 'react';
6
+ import { lazyLoad } from 'utils/loadable';
7
+ import styled from 'styled-components/macro';
8
+
9
+ const LoadingWrapper = styled.div`
10
+ width: 100%;
11
+ height: 100vh;
12
+ display: flex;
13
+ align-items: center;
14
+ justify-content: center;
15
+ `;
16
+
17
+ export const NamespacePage = lazyLoad(
18
+ () => import('./index'),
19
+ module => module.NamespacePage,
20
+ {
21
+ fallback: <LoadingWrapper></LoadingWrapper>,
22
+ },
23
+ );
@@ -0,0 +1,45 @@
1
+ // Jest Snapshot v1, https://goo.gl/fbAQLP
2
+
3
+ exports[`<NamespacePage /> should render and match the snapshot 1`] = `
4
+ <div
5
+ className="mid"
6
+ >
7
+ <NamespaceHeader />
8
+ <div
9
+ className="card"
10
+ >
11
+ <div
12
+ className="card-header"
13
+ >
14
+ <h2>
15
+ Nodes
16
+ </h2>
17
+ <div
18
+ className="table-responsive"
19
+ >
20
+ <table
21
+ className="card-table table"
22
+ >
23
+ <thead>
24
+ <th>
25
+ Namespace
26
+ </th>
27
+ <th>
28
+ Name
29
+ </th>
30
+ <th>
31
+ Type
32
+ </th>
33
+ <th>
34
+ Status
35
+ </th>
36
+ <th>
37
+ Mode
38
+ </th>
39
+ </thead>
40
+ </table>
41
+ </div>
42
+ </div>
43
+ </div>
44
+ </div>
45
+ `;
@@ -0,0 +1,14 @@
1
+ import * as React from 'react';
2
+ import { createRenderer } from 'react-test-renderer/shallow';
3
+
4
+ import { NamespacePage } from '../index';
5
+
6
+ const renderer = createRenderer();
7
+
8
+ describe('<NamespacePage />', () => {
9
+ it('should render and match the snapshot', () => {
10
+ renderer.render(<NamespacePage />);
11
+ const renderedOutput = renderer.getRenderOutput();
12
+ expect(renderedOutput).toMatchSnapshot();
13
+ });
14
+ });