datajunction-ui 0.0.1-rc.9 → 0.0.2-3.dev1
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.
- package/.env +2 -0
- package/.prettierignore +3 -1
- package/Makefile +9 -0
- package/dj-logo.svg +10 -0
- package/package.json +53 -14
- package/public/favicon.ico +0 -0
- package/public/index.html +1 -1
- package/src/__tests__/reportWebVitals.test.jsx +44 -0
- package/src/app/__tests__/__snapshots__/index.test.tsx.snap +5 -109
- package/src/app/components/AddNodeDropdown.jsx +44 -0
- package/src/app/components/ListGroupItem.jsx +9 -1
- package/src/app/components/NamespaceHeader.jsx +4 -13
- package/src/app/components/NodeListActions.jsx +69 -0
- package/src/app/components/NodeMaterializationDelete.jsx +90 -0
- package/src/app/components/NotificationBell.tsx +229 -0
- package/src/app/components/QueryInfo.jsx +172 -0
- package/src/app/components/Search.jsx +94 -0
- package/src/app/components/Tab.jsx +8 -1
- package/src/app/components/ToggleSwitch.jsx +20 -0
- package/src/app/components/UserMenu.tsx +92 -0
- package/src/app/components/__tests__/NodeListActions.test.jsx +94 -0
- package/src/app/components/__tests__/NodeMaterializationDelete.test.jsx +263 -0
- package/src/app/components/__tests__/NotificationBell.test.tsx +313 -0
- package/src/app/components/__tests__/QueryInfo.test.jsx +183 -0
- package/src/app/components/__tests__/Search.test.jsx +307 -0
- package/src/app/components/__tests__/Tab.test.jsx +27 -0
- package/src/app/components/__tests__/ToggleSwitch.test.jsx +43 -0
- package/src/app/components/__tests__/UserMenu.test.tsx +248 -0
- package/src/app/components/__tests__/__snapshots__/ListGroupItem.test.tsx.snap +8 -3
- package/src/app/components/__tests__/__snapshots__/NamespaceHeader.test.jsx.snap +2 -18
- package/src/app/components/djgraph/Collapse.jsx +47 -0
- package/src/app/components/djgraph/DJNode.jsx +61 -83
- package/src/app/components/djgraph/DJNodeColumns.jsx +75 -0
- package/src/app/components/djgraph/DJNodeDimensions.jsx +75 -0
- package/src/app/components/djgraph/LayoutFlow.jsx +106 -0
- package/src/app/components/djgraph/__tests__/Collapse.test.jsx +51 -0
- package/src/app/components/djgraph/__tests__/DJNodeColumns.test.jsx +83 -0
- package/src/app/components/djgraph/__tests__/DJNodeDimensions.test.jsx +118 -0
- package/src/app/components/djgraph/__tests__/__snapshots__/DJNode.test.tsx.snap +84 -40
- package/src/app/components/forms/Action.jsx +8 -0
- package/src/app/components/forms/NodeNameField.jsx +64 -0
- package/src/app/components/search.css +17 -0
- package/src/app/constants.js +2 -0
- package/src/app/icons/AddItemIcon.jsx +16 -0
- package/src/app/icons/AlertIcon.jsx +33 -0
- package/src/app/icons/CollapsedIcon.jsx +15 -0
- package/src/app/icons/CommitIcon.jsx +45 -0
- package/src/app/icons/DJLogo.jsx +36 -0
- package/src/app/icons/DeleteIcon.jsx +21 -0
- package/src/app/icons/DiffIcon.jsx +63 -0
- package/src/app/icons/EditIcon.jsx +18 -0
- package/src/app/icons/ExpandedIcon.jsx +15 -0
- package/src/app/icons/EyeIcon.jsx +20 -0
- package/src/app/icons/FilterIcon.jsx +7 -0
- package/src/app/icons/HorizontalHierarchyIcon.jsx +15 -0
- package/src/app/icons/InvalidIcon.jsx +16 -0
- package/src/app/icons/JupyterExportIcon.jsx +25 -0
- package/src/app/icons/LoadingIcon.jsx +14 -0
- package/src/app/icons/NodeIcon.jsx +49 -0
- package/src/app/icons/NotificationIcon.jsx +27 -0
- package/src/app/icons/PythonIcon.jsx +14 -0
- package/src/app/icons/SettingsIcon.jsx +28 -0
- package/src/app/icons/TableIcon.jsx +14 -0
- package/src/app/icons/ValidIcon.jsx +16 -0
- package/src/app/index.tsx +138 -38
- package/src/app/pages/AddEditNodePage/AlertMessage.jsx +10 -0
- package/src/app/pages/AddEditNodePage/ColumnsSelect.jsx +84 -0
- package/src/app/pages/AddEditNodePage/CustomMetadataField.jsx +144 -0
- package/src/app/pages/AddEditNodePage/DescriptionField.jsx +17 -0
- package/src/app/pages/AddEditNodePage/DisplayNameField.jsx +16 -0
- package/src/app/pages/AddEditNodePage/FormikSelect.jsx +64 -0
- package/src/app/pages/AddEditNodePage/FullNameField.jsx +38 -0
- package/src/app/pages/AddEditNodePage/Loadable.jsx +20 -0
- package/src/app/pages/AddEditNodePage/MetricMetadataFields.jsx +75 -0
- package/src/app/pages/AddEditNodePage/MetricQueryField.jsx +71 -0
- package/src/app/pages/AddEditNodePage/NamespaceField.jsx +40 -0
- package/src/app/pages/AddEditNodePage/NodeModeField.jsx +14 -0
- package/src/app/pages/AddEditNodePage/NodeQueryField.jsx +94 -0
- package/src/app/pages/AddEditNodePage/OwnersField.jsx +53 -0
- package/src/app/pages/AddEditNodePage/RequiredDimensionsSelect.jsx +54 -0
- package/src/app/pages/AddEditNodePage/TagsField.jsx +47 -0
- package/src/app/pages/AddEditNodePage/UpstreamNodeField.jsx +49 -0
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormFailed.test.jsx +110 -0
- package/src/app/pages/AddEditNodePage/__tests__/AddEditNodePageFormSuccess.test.jsx +291 -0
- package/src/app/pages/AddEditNodePage/__tests__/FormikSelect.test.jsx +75 -0
- package/src/app/pages/AddEditNodePage/__tests__/FullNameField.test.jsx +31 -0
- package/src/app/pages/AddEditNodePage/__tests__/NodeQueryField.test.jsx +30 -0
- package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormFailed.test.jsx.snap +54 -0
- package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/AddEditNodePageFormSuccess.test.jsx.snap +3 -0
- package/src/app/pages/AddEditNodePage/__tests__/__snapshots__/index.test.jsx.snap +3 -0
- package/src/app/pages/AddEditNodePage/__tests__/index.test.jsx +224 -0
- package/src/app/pages/AddEditNodePage/index.jsx +545 -0
- package/src/app/pages/AddEditTagPage/Loadable.jsx +16 -0
- package/src/app/pages/AddEditTagPage/__tests__/AddEditTagPage.test.jsx +107 -0
- package/src/app/pages/AddEditTagPage/index.jsx +132 -0
- package/src/app/pages/CubeBuilderPage/DimensionsSelect.jsx +152 -0
- package/src/app/pages/CubeBuilderPage/Loadable.jsx +16 -0
- package/src/app/pages/CubeBuilderPage/MetricsSelect.jsx +75 -0
- package/src/app/pages/CubeBuilderPage/__tests__/index.test.jsx +373 -0
- package/src/app/pages/CubeBuilderPage/index.jsx +291 -0
- package/src/app/pages/LoginPage/LoginForm.jsx +124 -0
- package/src/app/pages/LoginPage/SignupForm.jsx +156 -0
- package/src/app/pages/LoginPage/__tests__/index.test.jsx +97 -0
- package/src/app/pages/LoginPage/assets/sign-in-with-github.png +0 -0
- package/src/app/pages/LoginPage/assets/sign-in-with-google.png +0 -0
- package/src/app/pages/LoginPage/index.jsx +17 -0
- package/src/app/pages/NamespacePage/AddNamespacePopover.jsx +85 -0
- package/src/app/pages/NamespacePage/Explorer.jsx +232 -0
- package/src/app/pages/NamespacePage/FieldControl.jsx +21 -0
- package/src/app/pages/NamespacePage/NodeModeSelect.jsx +27 -0
- package/src/app/pages/NamespacePage/NodeTypeSelect.jsx +30 -0
- package/src/app/pages/NamespacePage/TagSelect.jsx +44 -0
- package/src/app/pages/NamespacePage/UserSelect.jsx +47 -0
- package/src/app/pages/NamespacePage/__tests__/AddNamespacePopover.test.jsx +283 -0
- package/src/app/pages/NamespacePage/__tests__/index.test.jsx +331 -0
- package/src/app/pages/NamespacePage/index.jsx +354 -42
- package/src/app/pages/NodePage/AddBackfillPopover.jsx +165 -0
- package/src/app/pages/NodePage/AddComplexDimensionLinkPopover.jsx +367 -0
- package/src/app/pages/NodePage/AddMaterializationPopover.jsx +222 -0
- package/src/app/pages/NodePage/AvailabilityStateBlock.jsx +67 -0
- package/src/app/pages/NodePage/ClientCodePopover.jsx +116 -0
- package/src/app/pages/NodePage/DimensionFilter.jsx +86 -0
- package/src/app/pages/NodePage/EditColumnDescriptionPopover.jsx +116 -0
- package/src/app/pages/NodePage/EditColumnPopover.jsx +116 -0
- package/src/app/pages/NodePage/LinkDimensionPopover.jsx +164 -0
- package/src/app/pages/NodePage/ManageDimensionLinksDialog.jsx +526 -0
- package/src/app/pages/NodePage/MaterializationConfigField.jsx +60 -0
- package/src/app/pages/NodePage/NodeColumnTab.jsx +421 -30
- package/src/app/pages/NodePage/NodeDependenciesTab.jsx +155 -0
- package/src/app/pages/NodePage/NodeGraphTab.jsx +119 -148
- package/src/app/pages/NodePage/NodeHistory.jsx +236 -0
- package/src/app/pages/NodePage/NodeInfoTab.jsx +404 -49
- package/src/app/pages/NodePage/NodeLineageTab.jsx +84 -0
- package/src/app/pages/NodePage/NodeMaterializationTab.jsx +585 -0
- package/src/app/pages/NodePage/NodeRevisionMaterializationTab.jsx +58 -0
- package/src/app/pages/NodePage/NodeStatus.jsx +100 -31
- package/src/app/pages/NodePage/NodeValidateTab.jsx +367 -0
- package/src/app/pages/NodePage/NodesWithDimension.jsx +42 -0
- package/src/app/pages/NodePage/NotebookDownload.jsx +36 -0
- package/src/app/pages/NodePage/PartitionColumnPopover.jsx +151 -0
- package/src/app/pages/NodePage/PartitionValueForm.jsx +60 -0
- package/src/app/pages/NodePage/RevisionDiff.jsx +209 -0
- package/src/app/pages/NodePage/WatchNodeButton.jsx +226 -0
- package/src/app/pages/NodePage/__tests__/AddBackfillPopover.test.jsx +56 -0
- package/src/app/pages/NodePage/__tests__/AddComplexDimensionLinkPopover.test.jsx +459 -0
- package/src/app/pages/NodePage/__tests__/AddMaterializationPopover.test.jsx +87 -0
- package/src/app/pages/NodePage/__tests__/DimensionFilter.test.jsx +74 -0
- package/src/app/pages/NodePage/__tests__/EditColumnDescriptionPopover.test.jsx +149 -0
- package/src/app/pages/NodePage/__tests__/EditColumnPopover.test.jsx +144 -0
- package/src/app/pages/NodePage/__tests__/LinkDimensionPopover.test.jsx +132 -0
- package/src/app/pages/NodePage/__tests__/ManageDimensionLinksDialog.test.jsx +390 -0
- package/src/app/pages/NodePage/__tests__/NodeColumnTab.test.jsx +166 -0
- package/src/app/pages/NodePage/__tests__/NodeDependenciesTab.test.jsx +157 -0
- package/src/app/pages/NodePage/__tests__/NodeGraphTab.test.jsx +595 -0
- package/src/app/pages/NodePage/__tests__/NodeLineageTab.test.jsx +58 -0
- package/src/app/pages/NodePage/__tests__/NodeMaterializationTab.test.jsx +190 -0
- package/src/app/pages/NodePage/__tests__/NodePage.test.jsx +892 -0
- package/src/app/pages/NodePage/__tests__/NodeWithDimension.test.jsx +175 -0
- package/src/app/pages/NodePage/__tests__/RevisionDiff.test.jsx +164 -0
- package/src/app/pages/NodePage/__tests__/__snapshots__/NodePage.test.jsx.snap +19 -0
- package/src/app/pages/NodePage/index.jsx +186 -45
- package/src/app/pages/NotFoundPage/__tests__/index.test.jsx +16 -0
- package/src/app/pages/NotificationsPage/Loadable.jsx +6 -0
- package/src/app/pages/NotificationsPage/__tests__/index.test.jsx +287 -0
- package/src/app/pages/NotificationsPage/index.jsx +136 -0
- package/src/app/pages/OverviewPage/ByStatusPanel.jsx +69 -0
- package/src/app/pages/OverviewPage/DimensionNodeUsagePanel.jsx +48 -0
- package/src/app/pages/OverviewPage/GovernanceWarningsPanel.jsx +107 -0
- package/src/app/pages/OverviewPage/Loadable.jsx +16 -0
- package/src/app/pages/OverviewPage/NodesByTypePanel.jsx +63 -0
- package/src/app/pages/OverviewPage/OverviewPanel.jsx +94 -0
- package/src/app/pages/OverviewPage/TrendsPanel.jsx +66 -0
- package/src/app/pages/OverviewPage/__tests__/ByStatusPanel.test.jsx +36 -0
- package/src/app/pages/OverviewPage/__tests__/DimensionNodeUsagePanel.test.jsx +76 -0
- package/src/app/pages/OverviewPage/__tests__/GovernanceWarningsPanel.test.jsx +77 -0
- package/src/app/pages/OverviewPage/__tests__/NodesByTypePanel.test.jsx +86 -0
- package/src/app/pages/OverviewPage/__tests__/OverviewPanel.test.jsx +78 -0
- package/src/app/pages/OverviewPage/__tests__/TrendsPanel.test.jsx +120 -0
- package/src/app/pages/OverviewPage/__tests__/index.test.jsx +54 -0
- package/src/app/pages/OverviewPage/index.jsx +22 -0
- package/src/app/pages/RegisterTablePage/Loadable.jsx +16 -0
- package/src/app/pages/RegisterTablePage/__tests__/RegisterTablePage.test.jsx +112 -0
- package/src/app/pages/RegisterTablePage/__tests__/__snapshots__/RegisterTablePage.test.jsx.snap +38 -0
- package/src/app/pages/RegisterTablePage/index.jsx +142 -0
- package/src/app/pages/Root/__tests__/index.test.jsx +44 -0
- package/src/app/pages/Root/index.tsx +92 -10
- package/src/app/pages/SQLBuilderPage/Loadable.jsx +16 -0
- package/src/app/pages/SQLBuilderPage/__tests__/index.test.jsx +173 -0
- package/src/app/pages/SQLBuilderPage/index.jsx +390 -0
- package/src/app/pages/SettingsPage/CreateServiceAccountModal.jsx +152 -0
- package/src/app/pages/SettingsPage/Loadable.jsx +16 -0
- package/src/app/pages/SettingsPage/NotificationSubscriptionsSection.jsx +189 -0
- package/src/app/pages/SettingsPage/ProfileSection.jsx +41 -0
- package/src/app/pages/SettingsPage/ServiceAccountsSection.jsx +95 -0
- package/src/app/pages/SettingsPage/__tests__/CreateServiceAccountModal.test.jsx +318 -0
- package/src/app/pages/SettingsPage/__tests__/NotificationSubscriptionsSection.test.jsx +233 -0
- package/src/app/pages/SettingsPage/__tests__/ProfileSection.test.jsx +65 -0
- package/src/app/pages/SettingsPage/__tests__/ServiceAccountsSection.test.jsx +150 -0
- package/src/app/pages/SettingsPage/__tests__/index.test.jsx +187 -0
- package/src/app/pages/SettingsPage/index.jsx +148 -0
- package/src/app/pages/TagPage/Loadable.jsx +16 -0
- package/src/app/pages/TagPage/__tests__/TagPage.test.jsx +70 -0
- package/src/app/pages/TagPage/index.jsx +79 -0
- package/src/app/providers/UserProvider.tsx +78 -0
- package/src/app/services/DJService.js +1487 -21
- package/src/app/services/__tests__/DJService.test.jsx +2194 -0
- package/src/app/utils/__tests__/date.test.js +198 -0
- package/src/app/utils/date.js +65 -0
- package/src/index.tsx +1 -0
- package/src/mocks/mockNodes.jsx +1477 -0
- package/src/setupTests.ts +31 -1
- package/src/styles/dag.css +117 -5
- package/src/styles/index.css +1028 -31
- package/src/styles/loading.css +34 -0
- package/src/styles/login.css +81 -0
- package/src/styles/nav-bar.css +274 -0
- package/src/styles/node-creation.scss +276 -0
- package/src/styles/node-list.css +4 -0
- package/src/styles/overview.css +72 -0
- package/src/styles/settings.css +787 -0
- package/src/styles/sorted-table.css +15 -0
- package/src/styles/styles.scss +44 -0
- package/src/styles/styles.scss.d.ts +9 -0
- package/src/utils/form.jsx +23 -0
- package/webpack.config.js +20 -7
- package/.babelrc +0 -4
- package/.env.local +0 -4
- package/.env.production +0 -1
- package/.github/pull_request_template.md +0 -11
- package/.github/workflows/ci.yml +0 -33
- package/.vscode/extensions.json +0 -7
- package/.vscode/launch.json +0 -15
- package/.vscode/settings.json +0 -25
- package/Dockerfile +0 -7
- package/src/app/pages/ListNamespacesPage/Loadable.jsx +0 -14
- package/src/app/pages/ListNamespacesPage/index.jsx +0 -62
- package/src/app/pages/NamespacePage/__tests__/__snapshots__/index.test.tsx.snap +0 -45
- package/src/app/pages/NamespacePage/__tests__/index.test.tsx +0 -14
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { screen, waitFor } from '@testing-library/react';
|
|
3
|
+
import fetchMock from 'jest-fetch-mock';
|
|
4
|
+
import userEvent from '@testing-library/user-event';
|
|
5
|
+
import { render } from '../../../setupTests';
|
|
6
|
+
import DJClientContext from '../../providers/djclient';
|
|
7
|
+
import NodeListActions from '../NodeListActions';
|
|
8
|
+
|
|
9
|
+
describe('<NodeListActions />', () => {
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
fetchMock.resetMocks();
|
|
12
|
+
jest.clearAllMocks();
|
|
13
|
+
window.scrollTo = jest.fn();
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
const renderElement = djClient => {
|
|
17
|
+
return render(
|
|
18
|
+
<DJClientContext.Provider value={djClient}>
|
|
19
|
+
<NodeListActions nodeName="default.hard_hat" />
|
|
20
|
+
</DJClientContext.Provider>,
|
|
21
|
+
);
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const initializeMockDJClient = () => {
|
|
25
|
+
return {
|
|
26
|
+
DataJunctionAPI: {
|
|
27
|
+
deactivate: jest.fn(),
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
it('deletes a node when clicked', async () => {
|
|
33
|
+
global.confirm = () => true;
|
|
34
|
+
const mockDjClient = initializeMockDJClient();
|
|
35
|
+
mockDjClient.DataJunctionAPI.deactivate.mockReturnValue({
|
|
36
|
+
status: 204,
|
|
37
|
+
json: { name: 'source.warehouse.schema.some_table' },
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
renderElement(mockDjClient);
|
|
41
|
+
|
|
42
|
+
await userEvent.click(screen.getByRole('button'));
|
|
43
|
+
|
|
44
|
+
await waitFor(() => {
|
|
45
|
+
expect(mockDjClient.DataJunctionAPI.deactivate).toBeCalled();
|
|
46
|
+
expect(mockDjClient.DataJunctionAPI.deactivate).toBeCalledWith(
|
|
47
|
+
'default.hard_hat',
|
|
48
|
+
);
|
|
49
|
+
});
|
|
50
|
+
expect(
|
|
51
|
+
screen.getByText('Successfully deleted node default.hard_hat'),
|
|
52
|
+
).toBeInTheDocument();
|
|
53
|
+
}, 60000);
|
|
54
|
+
|
|
55
|
+
it('skips a node deletion during confirm', async () => {
|
|
56
|
+
global.confirm = () => false;
|
|
57
|
+
const mockDjClient = initializeMockDJClient();
|
|
58
|
+
mockDjClient.DataJunctionAPI.deactivate.mockReturnValue({
|
|
59
|
+
status: 204,
|
|
60
|
+
json: { name: 'source.warehouse.schema.some_table' },
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
renderElement(mockDjClient);
|
|
64
|
+
|
|
65
|
+
await userEvent.click(screen.getByRole('button'));
|
|
66
|
+
|
|
67
|
+
await waitFor(() => {
|
|
68
|
+
expect(mockDjClient.DataJunctionAPI.deactivate).not.toBeCalled();
|
|
69
|
+
});
|
|
70
|
+
}, 60000);
|
|
71
|
+
|
|
72
|
+
it('fail deleting a node when clicked', async () => {
|
|
73
|
+
global.confirm = () => true;
|
|
74
|
+
const mockDjClient = initializeMockDJClient();
|
|
75
|
+
mockDjClient.DataJunctionAPI.deactivate.mockReturnValue({
|
|
76
|
+
status: 777,
|
|
77
|
+
json: { message: 'source.warehouse.schema.some_table' },
|
|
78
|
+
});
|
|
79
|
+
|
|
80
|
+
renderElement(mockDjClient);
|
|
81
|
+
|
|
82
|
+
await userEvent.click(screen.getByRole('button'));
|
|
83
|
+
|
|
84
|
+
await waitFor(() => {
|
|
85
|
+
expect(mockDjClient.DataJunctionAPI.deactivate).toBeCalled();
|
|
86
|
+
expect(mockDjClient.DataJunctionAPI.deactivate).toBeCalledWith(
|
|
87
|
+
'default.hard_hat',
|
|
88
|
+
);
|
|
89
|
+
});
|
|
90
|
+
expect(
|
|
91
|
+
screen.getByText('source.warehouse.schema.some_table'),
|
|
92
|
+
).toBeInTheDocument();
|
|
93
|
+
}, 60000);
|
|
94
|
+
});
|
|
@@ -0,0 +1,263 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, fireEvent, waitFor, act } from '@testing-library/react';
|
|
3
|
+
import NodeMaterializationDelete from '../NodeMaterializationDelete';
|
|
4
|
+
import DJClientContext from '../../providers/djclient';
|
|
5
|
+
|
|
6
|
+
// Mock window.location.reload
|
|
7
|
+
delete window.location;
|
|
8
|
+
window.location = { reload: jest.fn() };
|
|
9
|
+
|
|
10
|
+
// Mock window.confirm
|
|
11
|
+
window.confirm = jest.fn();
|
|
12
|
+
|
|
13
|
+
const mockDjClient = {
|
|
14
|
+
DataJunctionAPI: {
|
|
15
|
+
deleteMaterialization: jest.fn(),
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
describe('<NodeMaterializationDelete />', () => {
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
jest.clearAllMocks();
|
|
22
|
+
window.confirm.mockReturnValue(true);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
const defaultProps = {
|
|
26
|
+
nodeName: 'default.test_node',
|
|
27
|
+
materializationName: 'test_materialization',
|
|
28
|
+
nodeVersion: 'v1.0',
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
it('renders delete button', () => {
|
|
32
|
+
const { container } = render(
|
|
33
|
+
<DJClientContext.Provider value={mockDjClient}>
|
|
34
|
+
<NodeMaterializationDelete {...defaultProps} />
|
|
35
|
+
</DJClientContext.Provider>,
|
|
36
|
+
);
|
|
37
|
+
|
|
38
|
+
const deleteButton = container.querySelector('button[type="submit"]');
|
|
39
|
+
expect(deleteButton).toBeInTheDocument();
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
it('renders with null nodeVersion', () => {
|
|
43
|
+
const { container } = render(
|
|
44
|
+
<DJClientContext.Provider value={mockDjClient}>
|
|
45
|
+
<NodeMaterializationDelete
|
|
46
|
+
nodeName="default.test_node"
|
|
47
|
+
materializationName="test_materialization"
|
|
48
|
+
nodeVersion={null}
|
|
49
|
+
/>
|
|
50
|
+
</DJClientContext.Provider>,
|
|
51
|
+
);
|
|
52
|
+
|
|
53
|
+
const deleteButton = container.querySelector('button[type="submit"]');
|
|
54
|
+
expect(deleteButton).toBeInTheDocument();
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
it('shows confirm dialog when delete button is clicked', async () => {
|
|
58
|
+
const { container } = render(
|
|
59
|
+
<DJClientContext.Provider value={mockDjClient}>
|
|
60
|
+
<NodeMaterializationDelete {...defaultProps} />
|
|
61
|
+
</DJClientContext.Provider>,
|
|
62
|
+
);
|
|
63
|
+
|
|
64
|
+
const deleteButton = container.querySelector('button[type="submit"]');
|
|
65
|
+
|
|
66
|
+
await act(async () => {
|
|
67
|
+
fireEvent.click(deleteButton);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
expect(window.confirm).toHaveBeenCalledWith(
|
|
71
|
+
expect.stringContaining(
|
|
72
|
+
'Deleting materialization job test_materialization',
|
|
73
|
+
),
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
it('does not call deleteMaterialization when user cancels confirm', async () => {
|
|
78
|
+
window.confirm.mockReturnValueOnce(false);
|
|
79
|
+
|
|
80
|
+
const { container } = render(
|
|
81
|
+
<DJClientContext.Provider value={mockDjClient}>
|
|
82
|
+
<NodeMaterializationDelete {...defaultProps} />
|
|
83
|
+
</DJClientContext.Provider>,
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
const deleteButton = container.querySelector('button[type="submit"]');
|
|
87
|
+
|
|
88
|
+
await act(async () => {
|
|
89
|
+
fireEvent.click(deleteButton);
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
expect(window.confirm).toHaveBeenCalled();
|
|
93
|
+
expect(
|
|
94
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization,
|
|
95
|
+
).not.toHaveBeenCalled();
|
|
96
|
+
});
|
|
97
|
+
|
|
98
|
+
it('calls deleteMaterialization with correct params on success - status 200', async () => {
|
|
99
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization.mockResolvedValue({
|
|
100
|
+
status: 200,
|
|
101
|
+
json: { message: 'Deleted successfully' },
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
const { container, getByText } = render(
|
|
105
|
+
<DJClientContext.Provider value={mockDjClient}>
|
|
106
|
+
<NodeMaterializationDelete {...defaultProps} />
|
|
107
|
+
</DJClientContext.Provider>,
|
|
108
|
+
);
|
|
109
|
+
|
|
110
|
+
const deleteButton = container.querySelector('button[type="submit"]');
|
|
111
|
+
|
|
112
|
+
await act(async () => {
|
|
113
|
+
fireEvent.click(deleteButton);
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
await waitFor(() => {
|
|
117
|
+
expect(
|
|
118
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization,
|
|
119
|
+
).toHaveBeenCalledWith(
|
|
120
|
+
'default.test_node',
|
|
121
|
+
'test_materialization',
|
|
122
|
+
'v1.0',
|
|
123
|
+
);
|
|
124
|
+
expect(
|
|
125
|
+
getByText(/Successfully deleted materialization job/),
|
|
126
|
+
).toBeInTheDocument();
|
|
127
|
+
expect(window.location.reload).toHaveBeenCalled();
|
|
128
|
+
});
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('calls deleteMaterialization with correct params on success - status 201', async () => {
|
|
132
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization.mockResolvedValue({
|
|
133
|
+
status: 201,
|
|
134
|
+
json: { message: 'Deleted successfully' },
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
const { container } = render(
|
|
138
|
+
<DJClientContext.Provider value={mockDjClient}>
|
|
139
|
+
<NodeMaterializationDelete {...defaultProps} />
|
|
140
|
+
</DJClientContext.Provider>,
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const deleteButton = container.querySelector('button[type="submit"]');
|
|
144
|
+
|
|
145
|
+
await act(async () => {
|
|
146
|
+
fireEvent.click(deleteButton);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
await waitFor(() => {
|
|
150
|
+
expect(
|
|
151
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization,
|
|
152
|
+
).toHaveBeenCalled();
|
|
153
|
+
expect(window.location.reload).toHaveBeenCalled();
|
|
154
|
+
});
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
it('calls deleteMaterialization with correct params on success - status 204', async () => {
|
|
158
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization.mockResolvedValue({
|
|
159
|
+
status: 204,
|
|
160
|
+
json: {},
|
|
161
|
+
});
|
|
162
|
+
|
|
163
|
+
const { container } = render(
|
|
164
|
+
<DJClientContext.Provider value={mockDjClient}>
|
|
165
|
+
<NodeMaterializationDelete {...defaultProps} />
|
|
166
|
+
</DJClientContext.Provider>,
|
|
167
|
+
);
|
|
168
|
+
|
|
169
|
+
const deleteButton = container.querySelector('button[type="submit"]');
|
|
170
|
+
|
|
171
|
+
await act(async () => {
|
|
172
|
+
fireEvent.click(deleteButton);
|
|
173
|
+
});
|
|
174
|
+
|
|
175
|
+
await waitFor(() => {
|
|
176
|
+
expect(
|
|
177
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization,
|
|
178
|
+
).toHaveBeenCalled();
|
|
179
|
+
expect(window.location.reload).toHaveBeenCalled();
|
|
180
|
+
});
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('displays error message when deletion fails', async () => {
|
|
184
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization.mockResolvedValue({
|
|
185
|
+
status: 500,
|
|
186
|
+
json: { message: 'Internal server error' },
|
|
187
|
+
});
|
|
188
|
+
|
|
189
|
+
const { container, getByText } = render(
|
|
190
|
+
<DJClientContext.Provider value={mockDjClient}>
|
|
191
|
+
<NodeMaterializationDelete {...defaultProps} />
|
|
192
|
+
</DJClientContext.Provider>,
|
|
193
|
+
);
|
|
194
|
+
|
|
195
|
+
const deleteButton = container.querySelector('button[type="submit"]');
|
|
196
|
+
|
|
197
|
+
await act(async () => {
|
|
198
|
+
fireEvent.click(deleteButton);
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
await waitFor(() => {
|
|
202
|
+
expect(
|
|
203
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization,
|
|
204
|
+
).toHaveBeenCalled();
|
|
205
|
+
expect(getByText('Internal server error')).toBeInTheDocument();
|
|
206
|
+
expect(window.location.reload).not.toHaveBeenCalled();
|
|
207
|
+
});
|
|
208
|
+
});
|
|
209
|
+
|
|
210
|
+
it('hides delete button after successful deletion', async () => {
|
|
211
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization.mockResolvedValue({
|
|
212
|
+
status: 200,
|
|
213
|
+
json: { message: 'Deleted successfully' },
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
const { container } = render(
|
|
217
|
+
<DJClientContext.Provider value={mockDjClient}>
|
|
218
|
+
<NodeMaterializationDelete {...defaultProps} />
|
|
219
|
+
</DJClientContext.Provider>,
|
|
220
|
+
);
|
|
221
|
+
|
|
222
|
+
const deleteButton = container.querySelector('button[type="submit"]');
|
|
223
|
+
expect(deleteButton).toBeInTheDocument();
|
|
224
|
+
|
|
225
|
+
await act(async () => {
|
|
226
|
+
fireEvent.click(deleteButton);
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
await waitFor(() => {
|
|
230
|
+
expect(
|
|
231
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization,
|
|
232
|
+
).toHaveBeenCalled();
|
|
233
|
+
});
|
|
234
|
+
});
|
|
235
|
+
|
|
236
|
+
it('passes null nodeVersion to deleteMaterialization when not provided', async () => {
|
|
237
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization.mockResolvedValue({
|
|
238
|
+
status: 200,
|
|
239
|
+
json: { message: 'Deleted successfully' },
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
const { container } = render(
|
|
243
|
+
<DJClientContext.Provider value={mockDjClient}>
|
|
244
|
+
<NodeMaterializationDelete
|
|
245
|
+
nodeName="default.test_node"
|
|
246
|
+
materializationName="test_mat"
|
|
247
|
+
/>
|
|
248
|
+
</DJClientContext.Provider>,
|
|
249
|
+
);
|
|
250
|
+
|
|
251
|
+
const deleteButton = container.querySelector('button[type="submit"]');
|
|
252
|
+
|
|
253
|
+
await act(async () => {
|
|
254
|
+
fireEvent.click(deleteButton);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
await waitFor(() => {
|
|
258
|
+
expect(
|
|
259
|
+
mockDjClient.DataJunctionAPI.deleteMaterialization,
|
|
260
|
+
).toHaveBeenCalledWith('default.test_node', 'test_mat', null);
|
|
261
|
+
});
|
|
262
|
+
});
|
|
263
|
+
});
|
|
@@ -0,0 +1,313 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, screen, fireEvent, waitFor } from '@testing-library/react';
|
|
3
|
+
import NotificationBell from '../NotificationBell';
|
|
4
|
+
import DJClientContext from '../../providers/djclient';
|
|
5
|
+
import { UserProvider } from '../../providers/UserProvider';
|
|
6
|
+
|
|
7
|
+
describe('<NotificationBell />', () => {
|
|
8
|
+
const mockNotifications = [
|
|
9
|
+
{
|
|
10
|
+
id: 1,
|
|
11
|
+
entity_type: 'node',
|
|
12
|
+
entity_name: 'default.metrics.revenue',
|
|
13
|
+
node: 'default.metrics.revenue',
|
|
14
|
+
activity_type: 'update',
|
|
15
|
+
user: 'alice',
|
|
16
|
+
created_at: new Date().toISOString(),
|
|
17
|
+
details: { version: 'v2' },
|
|
18
|
+
},
|
|
19
|
+
{
|
|
20
|
+
id: 2,
|
|
21
|
+
entity_type: 'node',
|
|
22
|
+
entity_name: 'default.dimensions.country',
|
|
23
|
+
node: 'default.dimensions.country',
|
|
24
|
+
activity_type: 'create',
|
|
25
|
+
user: 'bob',
|
|
26
|
+
created_at: new Date(Date.now() - 3600000).toISOString(), // 1 hour ago
|
|
27
|
+
details: { version: 'v1' },
|
|
28
|
+
},
|
|
29
|
+
];
|
|
30
|
+
|
|
31
|
+
const mockNodes = [
|
|
32
|
+
{
|
|
33
|
+
name: 'default.metrics.revenue',
|
|
34
|
+
type: 'metric',
|
|
35
|
+
current: { displayName: 'Revenue Metric' },
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
name: 'default.dimensions.country',
|
|
39
|
+
type: 'dimension',
|
|
40
|
+
current: { displayName: 'Country' },
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
const createMockDjClient = (overrides = {}) => ({
|
|
45
|
+
whoami: jest.fn().mockResolvedValue({
|
|
46
|
+
id: 1,
|
|
47
|
+
username: 'testuser',
|
|
48
|
+
last_viewed_notifications_at: null,
|
|
49
|
+
}),
|
|
50
|
+
getSubscribedHistory: jest.fn().mockResolvedValue(mockNotifications),
|
|
51
|
+
getNodesByNames: jest.fn().mockResolvedValue(mockNodes),
|
|
52
|
+
markNotificationsRead: jest.fn().mockResolvedValue({}),
|
|
53
|
+
...overrides,
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
const renderWithContext = (mockDjClient: any, props = {}) => {
|
|
57
|
+
return render(
|
|
58
|
+
<DJClientContext.Provider value={{ DataJunctionAPI: mockDjClient }}>
|
|
59
|
+
<UserProvider>
|
|
60
|
+
<NotificationBell {...props} />
|
|
61
|
+
</UserProvider>
|
|
62
|
+
</DJClientContext.Provider>,
|
|
63
|
+
);
|
|
64
|
+
};
|
|
65
|
+
|
|
66
|
+
beforeEach(() => {
|
|
67
|
+
jest.clearAllMocks();
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('renders the notification bell button', async () => {
|
|
71
|
+
const mockDjClient = createMockDjClient();
|
|
72
|
+
renderWithContext(mockDjClient);
|
|
73
|
+
|
|
74
|
+
const button = screen.getByRole('button');
|
|
75
|
+
expect(button).toBeInTheDocument();
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
it('shows unread badge when there are unread notifications', async () => {
|
|
79
|
+
const mockDjClient = createMockDjClient();
|
|
80
|
+
renderWithContext(mockDjClient);
|
|
81
|
+
|
|
82
|
+
// Wait for notifications to load
|
|
83
|
+
await waitFor(() => {
|
|
84
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
// Badge should show count of 2 (all notifications are unread since last_viewed is null)
|
|
88
|
+
const badge = await screen.findByText('2');
|
|
89
|
+
expect(badge).toHaveClass('notification-badge');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('does not show badge when all notifications have been viewed', async () => {
|
|
93
|
+
const mockDjClient = createMockDjClient({
|
|
94
|
+
whoami: jest.fn().mockResolvedValue({
|
|
95
|
+
id: 1,
|
|
96
|
+
username: 'testuser',
|
|
97
|
+
// Set last_viewed to future date so all notifications are "read"
|
|
98
|
+
last_viewed_notifications_at: new Date(
|
|
99
|
+
Date.now() + 10000,
|
|
100
|
+
).toISOString(),
|
|
101
|
+
}),
|
|
102
|
+
});
|
|
103
|
+
renderWithContext(mockDjClient);
|
|
104
|
+
|
|
105
|
+
await waitFor(() => {
|
|
106
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
// Badge should not be present (no unread count shown)
|
|
110
|
+
const badge = document.querySelector('.notification-badge');
|
|
111
|
+
expect(badge).toBeNull();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('opens dropdown when bell is clicked', async () => {
|
|
115
|
+
const mockDjClient = createMockDjClient();
|
|
116
|
+
renderWithContext(mockDjClient);
|
|
117
|
+
|
|
118
|
+
await waitFor(() => {
|
|
119
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const button = screen.getByRole('button');
|
|
123
|
+
fireEvent.click(button);
|
|
124
|
+
|
|
125
|
+
expect(screen.getByText('Updates')).toBeInTheDocument();
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
it('displays notifications in the dropdown', async () => {
|
|
129
|
+
const mockDjClient = createMockDjClient();
|
|
130
|
+
renderWithContext(mockDjClient);
|
|
131
|
+
|
|
132
|
+
await waitFor(() => {
|
|
133
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
const button = screen.getByRole('button');
|
|
137
|
+
fireEvent.click(button);
|
|
138
|
+
|
|
139
|
+
// Check that display names are shown
|
|
140
|
+
expect(screen.getByText('Revenue Metric')).toBeInTheDocument();
|
|
141
|
+
expect(screen.getByText('Country')).toBeInTheDocument();
|
|
142
|
+
|
|
143
|
+
// Check that entity names are shown below
|
|
144
|
+
expect(screen.getByText('default.metrics.revenue')).toBeInTheDocument();
|
|
145
|
+
expect(screen.getByText('default.dimensions.country')).toBeInTheDocument();
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
it('shows empty state when no notifications', async () => {
|
|
149
|
+
const mockDjClient = createMockDjClient({
|
|
150
|
+
getSubscribedHistory: jest.fn().mockResolvedValue([]),
|
|
151
|
+
});
|
|
152
|
+
renderWithContext(mockDjClient);
|
|
153
|
+
|
|
154
|
+
await waitFor(() => {
|
|
155
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
const button = screen.getByRole('button');
|
|
159
|
+
fireEvent.click(button);
|
|
160
|
+
|
|
161
|
+
expect(screen.getByText('No updates on watched nodes')).toBeInTheDocument();
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('marks notifications as read when dropdown is opened', async () => {
|
|
165
|
+
const mockDjClient = createMockDjClient();
|
|
166
|
+
renderWithContext(mockDjClient);
|
|
167
|
+
|
|
168
|
+
await waitFor(() => {
|
|
169
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
const button = screen.getByRole('button');
|
|
173
|
+
fireEvent.click(button);
|
|
174
|
+
|
|
175
|
+
expect(mockDjClient.markNotificationsRead).toHaveBeenCalled();
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
it('does not mark as read if already all read', async () => {
|
|
179
|
+
const mockDjClient = createMockDjClient({
|
|
180
|
+
whoami: jest.fn().mockResolvedValue({
|
|
181
|
+
id: 1,
|
|
182
|
+
username: 'testuser',
|
|
183
|
+
last_viewed_notifications_at: new Date(
|
|
184
|
+
Date.now() + 10000,
|
|
185
|
+
).toISOString(),
|
|
186
|
+
}),
|
|
187
|
+
});
|
|
188
|
+
renderWithContext(mockDjClient);
|
|
189
|
+
|
|
190
|
+
await waitFor(() => {
|
|
191
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
const button = screen.getByRole('button');
|
|
195
|
+
fireEvent.click(button);
|
|
196
|
+
|
|
197
|
+
// Should not call markNotificationsRead since unreadCount is 0
|
|
198
|
+
expect(mockDjClient.markNotificationsRead).not.toHaveBeenCalled();
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
it('shows View all link when there are notifications', async () => {
|
|
202
|
+
const mockDjClient = createMockDjClient();
|
|
203
|
+
renderWithContext(mockDjClient);
|
|
204
|
+
|
|
205
|
+
await waitFor(() => {
|
|
206
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
const button = screen.getByRole('button');
|
|
210
|
+
fireEvent.click(button);
|
|
211
|
+
|
|
212
|
+
const viewAllLink = screen.getByText('View all');
|
|
213
|
+
expect(viewAllLink).toHaveAttribute('href', '/notifications');
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
it('calls onDropdownToggle when dropdown state changes', async () => {
|
|
217
|
+
const mockDjClient = createMockDjClient();
|
|
218
|
+
const onDropdownToggle = jest.fn();
|
|
219
|
+
|
|
220
|
+
render(
|
|
221
|
+
<DJClientContext.Provider
|
|
222
|
+
value={{ DataJunctionAPI: mockDjClient as any }}
|
|
223
|
+
>
|
|
224
|
+
<UserProvider>
|
|
225
|
+
<NotificationBell onDropdownToggle={onDropdownToggle} />
|
|
226
|
+
</UserProvider>
|
|
227
|
+
</DJClientContext.Provider>,
|
|
228
|
+
);
|
|
229
|
+
|
|
230
|
+
await waitFor(() => {
|
|
231
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
const button = screen.getByRole('button');
|
|
235
|
+
fireEvent.click(button);
|
|
236
|
+
|
|
237
|
+
expect(onDropdownToggle).toHaveBeenCalledWith(true);
|
|
238
|
+
});
|
|
239
|
+
|
|
240
|
+
it('closes dropdown when forceClose becomes true', async () => {
|
|
241
|
+
const mockDjClient = createMockDjClient();
|
|
242
|
+
|
|
243
|
+
const { rerender } = render(
|
|
244
|
+
<DJClientContext.Provider
|
|
245
|
+
value={{ DataJunctionAPI: mockDjClient as any }}
|
|
246
|
+
>
|
|
247
|
+
<UserProvider>
|
|
248
|
+
<NotificationBell forceClose={false} />
|
|
249
|
+
</UserProvider>
|
|
250
|
+
</DJClientContext.Provider>,
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
await waitFor(() => {
|
|
254
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
// Open the dropdown
|
|
258
|
+
const button = screen.getByRole('button');
|
|
259
|
+
fireEvent.click(button);
|
|
260
|
+
|
|
261
|
+
// Verify dropdown is open
|
|
262
|
+
expect(screen.getByText('Updates')).toBeInTheDocument();
|
|
263
|
+
|
|
264
|
+
// Rerender with forceClose=true
|
|
265
|
+
rerender(
|
|
266
|
+
<DJClientContext.Provider
|
|
267
|
+
value={{ DataJunctionAPI: mockDjClient as any }}
|
|
268
|
+
>
|
|
269
|
+
<UserProvider>
|
|
270
|
+
<NotificationBell forceClose={true} />
|
|
271
|
+
</UserProvider>
|
|
272
|
+
</DJClientContext.Provider>,
|
|
273
|
+
);
|
|
274
|
+
|
|
275
|
+
// Dropdown should be closed
|
|
276
|
+
expect(screen.queryByText('Updates')).not.toBeInTheDocument();
|
|
277
|
+
});
|
|
278
|
+
|
|
279
|
+
it('closes dropdown when clicking outside', async () => {
|
|
280
|
+
const mockDjClient = createMockDjClient();
|
|
281
|
+
const onDropdownToggle = jest.fn();
|
|
282
|
+
|
|
283
|
+
render(
|
|
284
|
+
<DJClientContext.Provider
|
|
285
|
+
value={{ DataJunctionAPI: mockDjClient as any }}
|
|
286
|
+
>
|
|
287
|
+
<UserProvider>
|
|
288
|
+
<NotificationBell onDropdownToggle={onDropdownToggle} />
|
|
289
|
+
</UserProvider>
|
|
290
|
+
</DJClientContext.Provider>,
|
|
291
|
+
);
|
|
292
|
+
|
|
293
|
+
await waitFor(() => {
|
|
294
|
+
expect(mockDjClient.getSubscribedHistory).toHaveBeenCalled();
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
// Open the dropdown
|
|
298
|
+
const button = screen.getByRole('button');
|
|
299
|
+
fireEvent.click(button);
|
|
300
|
+
|
|
301
|
+
// Verify dropdown is open
|
|
302
|
+
expect(screen.getByText('Updates')).toBeInTheDocument();
|
|
303
|
+
|
|
304
|
+
// Click outside the dropdown
|
|
305
|
+
fireEvent.click(document.body);
|
|
306
|
+
|
|
307
|
+
// Dropdown should be closed
|
|
308
|
+
expect(screen.queryByText('Updates')).not.toBeInTheDocument();
|
|
309
|
+
|
|
310
|
+
// onDropdownToggle should have been called with false
|
|
311
|
+
expect(onDropdownToggle).toHaveBeenCalledWith(false);
|
|
312
|
+
});
|
|
313
|
+
});
|