-
Notifications
You must be signed in to change notification settings - Fork 923
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
[Workspace]Import sample data to current workspace #6105
Changes from 25 commits
4321cb4
d29f691
54adb59
5240160
ee39682
b1da07e
b4c0c27
0d44b0f
e4a6c74
9b3be8f
5098c09
58adba2
0e7c5e1
f11844f
efc5b1c
8b67b94
e87cb72
799f475
fb4c3a2
5767ffc
eda80ff
dd998f3
7d7a831
a7e8c72
9769622
28d1f74
6162c2d
b82fcec
5f8f9f4
3f27d71
4d560c6
9a2bd3e
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
feat: | ||
- [Workspace]Import sample data to current workspace ([#6105](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/6105)) |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import React, { useEffect, useRef } from 'react'; | ||
import { render, waitFor } from '@testing-library/react'; | ||
import { coreMock, scopedHistoryMock } from '../../../../core/public/mocks'; | ||
import { renderImportSampleDataApp } from './application'; | ||
|
||
jest.mock('./components/home_app', () => ({ | ||
HomeApp: () => 'HomeApp', | ||
ImportSampleDataApp: () => 'ImportSampleDataApp', | ||
})); | ||
|
||
const coreStartMocks = coreMock.createStart(); | ||
|
||
const ComponentForRender = (props: { | ||
renderFn: typeof renderImportSampleDataApp; | ||
historyMock?: ReturnType<typeof scopedHistoryMock.create>; | ||
}) => { | ||
const container = useRef<HTMLDivElement>(null); | ||
const historyMock = props.historyMock || scopedHistoryMock.create(); | ||
historyMock.listen.mockReturnValueOnce(() => () => null); | ||
useEffect(() => { | ||
if (container.current) { | ||
const destroyFn = props.renderFn(container.current, coreStartMocks, historyMock); | ||
return () => { | ||
destroyFn.then((res) => res()); | ||
}; | ||
} | ||
}, [historyMock, props]); | ||
|
||
return <div ref={container} />; | ||
}; | ||
|
||
describe('renderImportSampleDataApp', () => { | ||
it('should render ImportSampleDataApp when calling renderImportSampleDataApp', async () => { | ||
const { container } = render(<ComponentForRender renderFn={renderImportSampleDataApp} />); | ||
expect(container).toMatchInlineSnapshot(` | ||
<div> | ||
<div> | ||
ImportSampleDataApp | ||
</div> | ||
</div> | ||
`); | ||
}); | ||
it('should call unlisten history after destroy', async () => { | ||
const historyMock = scopedHistoryMock.create(); | ||
const unlistenMock = jest.fn(() => null); | ||
historyMock.listen.mockImplementationOnce(() => unlistenMock); | ||
const { unmount } = render( | ||
<ComponentForRender renderFn={renderImportSampleDataApp} historyMock={historyMock} /> | ||
); | ||
unmount(); | ||
await waitFor(() => { | ||
expect(unlistenMock).toHaveBeenCalled(); | ||
}); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -34,7 +34,7 @@ | |
import { ScopedHistory, CoreStart } from 'opensearch-dashboards/public'; | ||
import { OpenSearchDashboardsContextProvider } from '../../../opensearch_dashboards_react/public'; | ||
// @ts-ignore | ||
import { HomeApp } from './components/home_app'; | ||
import { HomeApp, ImportSampleDataApp } from './components/home_app'; | ||
import { getServices } from './opensearch_dashboards_services'; | ||
|
||
import './index.scss'; | ||
|
@@ -77,3 +77,28 @@ | |
unlisten(); | ||
}; | ||
}; | ||
|
||
export const renderImportSampleDataApp = async ( | ||
element: HTMLElement, | ||
coreStart: CoreStart, | ||
history: ScopedHistory | ||
) => { | ||
// dispatch synthetic hash change event to update hash history objects | ||
// this is necessary because hash updates triggered by using popState won't trigger this event naturally. | ||
// This must be called before the app is mounted to avoid call this after the redirect to default app logic kicks in | ||
const unlisten = history.listen((location) => { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Do we need this for import sample data app? It seems it's home page app specific. |
||
window.dispatchEvent(new HashChangeEvent('hashchange')); | ||
}); | ||
|
||
render( | ||
<OpenSearchDashboardsContextProvider services={{ ...coreStart }}> | ||
<ImportSampleDataApp /> | ||
</OpenSearchDashboardsContextProvider>, | ||
element | ||
); | ||
|
||
return () => { | ||
unmountComponentAtNode(element); | ||
unlisten(); | ||
}; | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -51,6 +51,44 @@ const RedirectToDefaultApp = () => { | |
return null; | ||
}; | ||
|
||
const renderTutorialDirectory = (props) => { | ||
const { addBasePath, environmentService } = getServices(); | ||
const environment = environmentService.getEnvironment(); | ||
const isCloudEnabled = environment.cloud; | ||
|
||
return ( | ||
<TutorialDirectory | ||
addBasePath={addBasePath} | ||
openTab={props.match.params.tab} | ||
isCloudEnabled={isCloudEnabled} | ||
withoutHomeBreadCrumb={props.withoutHomeBreadCrumb} | ||
/> | ||
); | ||
}; | ||
|
||
export function ImportSampleDataApp() { | ||
return ( | ||
<I18nProvider> | ||
<Router> | ||
<Switch> | ||
<Route | ||
path="*" | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Q: why does it need a Route with path There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The |
||
exact={true} | ||
component={(props) => | ||
renderTutorialDirectory({ | ||
...props, | ||
// For standalone import sample data application | ||
// home breadcrumb should not be appended as it is not a sub app of home | ||
withoutHomeBreadCrumb: true, | ||
}) | ||
} | ||
/> | ||
</Switch> | ||
</Router> | ||
</I18nProvider> | ||
); | ||
} | ||
|
||
export function HomeApp({ directories, solutions }) { | ||
const { | ||
savedObjectsClient, | ||
|
@@ -63,16 +101,6 @@ export function HomeApp({ directories, solutions }) { | |
const environment = environmentService.getEnvironment(); | ||
const isCloudEnabled = environment.cloud; | ||
|
||
const renderTutorialDirectory = (props) => { | ||
return ( | ||
<TutorialDirectory | ||
addBasePath={addBasePath} | ||
openTab={props.match.params.tab} | ||
isCloudEnabled={isCloudEnabled} | ||
/> | ||
); | ||
}; | ||
|
||
const renderTutorial = (props) => { | ||
return ( | ||
<Tutorial | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,60 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import React from 'react'; | ||
import { render } from '@testing-library/react'; | ||
import { setServices } from '../opensearch_dashboards_services'; | ||
import { getMockedServices } from '../opensearch_dashboards_services.mock'; | ||
import { ImportSampleDataApp, HomeApp } from './home_app'; | ||
|
||
jest.mock('./legacy/home', () => ({ | ||
Home: () => <div>Home</div>, | ||
})); | ||
|
||
jest.mock('../load_tutorials', () => ({ | ||
getTutorial: () => {}, | ||
})); | ||
|
||
jest.mock('./tutorial_directory', () => ({ | ||
TutorialDirectory: (props: { withoutHomeBreadCrumb?: boolean }) => ( | ||
<div | ||
data-test-subj="tutorial_directory" | ||
data-without-home-bread-crumb={!!props.withoutHomeBreadCrumb} | ||
/> | ||
), | ||
})); | ||
|
||
describe('<HomeApp />', () => { | ||
let currentService: ReturnType<typeof getMockedServices>; | ||
beforeEach(() => { | ||
currentService = getMockedServices(); | ||
setServices(currentService); | ||
}); | ||
|
||
it('should not pass withoutHomeBreadCrumb to TutorialDirectory component', async () => { | ||
const originalHash = window.location.hash; | ||
const { findByTestId } = render(<HomeApp />); | ||
window.location.hash = '/tutorial_directory'; | ||
const tutorialRenderResult = await findByTestId('tutorial_directory'); | ||
expect(tutorialRenderResult.dataset.withoutHomeBreadCrumb).toEqual('false'); | ||
|
||
// revert to original hash | ||
window.location.hash = originalHash; | ||
}); | ||
}); | ||
|
||
describe('<ImportSampleDataApp />', () => { | ||
let currentService: ReturnType<typeof getMockedServices>; | ||
beforeEach(() => { | ||
currentService = getMockedServices(); | ||
setServices(currentService); | ||
}); | ||
|
||
it('should pass withoutHomeBreadCrumb to TutorialDirectory component', async () => { | ||
const { findByTestId } = render(<ImportSampleDataApp />); | ||
const tutorialRenderResult = await findByTestId('tutorial_directory'); | ||
expect(tutorialRenderResult.dataset.withoutHomeBreadCrumb).toEqual('true'); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import React from 'react'; | ||
import { render } from '@testing-library/react'; | ||
import { IntlProvider } from 'react-intl'; | ||
import { coreMock } from '../../../../../core/public/mocks'; | ||
import { setServices } from '../opensearch_dashboards_services'; | ||
import { getMockedServices } from '../opensearch_dashboards_services.mock'; | ||
|
||
const makeProps = () => { | ||
const coreMocks = coreMock.createStart(); | ||
return { | ||
addBasePath: coreMocks.http.basePath.prepend, | ||
openTab: 'foo', | ||
isCloudEnabled: false, | ||
}; | ||
}; | ||
|
||
describe('<TutorialDirectory />', () => { | ||
let currentService: ReturnType<typeof getMockedServices>; | ||
beforeEach(() => { | ||
currentService = getMockedServices(); | ||
setServices(currentService); | ||
}); | ||
it('should render home breadcrumbs when withoutHomeBreadCrumb is undefined', async () => { | ||
const finalProps = makeProps(); | ||
currentService.http.get.mockResolvedValueOnce([]); | ||
// @ts-ignore | ||
const { TutorialDirectory } = await import('./tutorial_directory'); | ||
render( | ||
<IntlProvider locale="en"> | ||
<TutorialDirectory {...finalProps} /> | ||
</IntlProvider> | ||
); | ||
expect(currentService.chrome.setBreadcrumbs).toBeCalledWith([ | ||
{ | ||
href: '#/', | ||
text: 'Home', | ||
}, | ||
{ | ||
text: 'Add data', | ||
}, | ||
]); | ||
}); | ||
|
||
it('should not render home breadcrumbs when withoutHomeBreadCrumb is true', async () => { | ||
const finalProps = makeProps(); | ||
currentService.http.get.mockResolvedValueOnce([]); | ||
// @ts-ignore | ||
const { TutorialDirectory } = await import('./tutorial_directory'); | ||
render( | ||
<IntlProvider locale="en"> | ||
<TutorialDirectory {...finalProps} withoutHomeBreadCrumb /> | ||
</IntlProvider> | ||
); | ||
expect(currentService.chrome.setBreadcrumbs).toBeCalledWith([ | ||
{ | ||
text: 'Add data', | ||
}, | ||
]); | ||
}); | ||
}); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,47 @@ | ||
/* | ||
* Copyright OpenSearch Contributors | ||
* SPDX-License-Identifier: Apache-2.0 | ||
*/ | ||
|
||
import { coreMock } from '../../../../core/public/mocks'; | ||
import { urlForwardingPluginMock } from '../../../url_forwarding/public/mocks'; | ||
import { homePluginMock } from '../mocks'; | ||
import { | ||
EnvironmentService, | ||
FeatureCatalogueRegistry, | ||
SectionTypeService, | ||
TutorialService, | ||
} from '../services'; | ||
import { telemetryPluginMock } from '../../../telemetry/public/mocks'; | ||
|
||
export const getMockedServices = () => { | ||
const coreMocks = coreMock.createStart(); | ||
const urlForwarding = urlForwardingPluginMock.createStartContract(); | ||
const homePlugin = homePluginMock.createSetupContract(); | ||
return { | ||
...coreMocks, | ||
...homePlugin, | ||
telemetry: telemetryPluginMock.createStartContract(), | ||
indexPatternService: jest.fn(), | ||
dataSource: { | ||
dataSourceEnabled: false, | ||
hideLocalCluster: false, | ||
noAuthenticationTypeEnabled: false, | ||
usernamePasswordAuthEnabled: false, | ||
awsSigV4AuthEnabled: false, | ||
}, | ||
opensearchDashboardsVersion: '', | ||
urlForwarding, | ||
savedObjectsClient: coreMocks.savedObjects.client, | ||
toastNotifications: coreMocks.notifications.toasts, | ||
banners: coreMocks.overlays.banners, | ||
trackUiMetric: jest.fn(), | ||
getBasePath: jest.fn(), | ||
addBasePath: jest.fn(), | ||
environmentService: new EnvironmentService(), | ||
tutorialService: new TutorialService(), | ||
homeConfig: homePlugin.config, | ||
featureCatalogue: new FeatureCatalogueRegistry(), | ||
sectionTypes: new SectionTypeService(), | ||
}; | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe like "should clean up history listeners after unmount" ?