Skip to content
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] Add unit tests for inspect page url #8834

Merged
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 2 additions & 0 deletions changelogs/fragments/8834.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
refactor:
- [Workspace] Add unit tests for inspect page url ([#8834](https://github.com/opensearch-project/OpenSearch-Dashboards/pull/8834))
Original file line number Diff line number Diff line change
@@ -0,0 +1,123 @@
/*
* Copyright OpenSearch Contributors
* SPDX-License-Identifier: Apache-2.0
*/

import React from 'react';
import { render } from '@testing-library/react';
import { createOpenSearchDashboardsReactContext } from '../../../../../opensearch_dashboards_react/public';
import { coreMock, workspacesServiceMock } from '../../../../../../core/public/mocks';
import { BehaviorSubject } from 'rxjs';
import { WorkspaceObject } from 'opensearch-dashboards/public';
import { IntlProvider } from 'react-intl';
import { IndexHeader } from './index_header';

describe('IndexHeader at new home page', () => {
const indexPattern = { id: 'default-index', title: 'Test Index Pattern', fields: [] };
const mockCoreStart = coreMock.createStart();
const workspaceObject = {
id: 'foo_id',
name: 'foo',
};
const getIndexHeader = (props: any) => {
const mockHeaderControl =
(props?.header as Function) ||
(() => {
return null;
});

const setDefault = jest.fn();
const refreshFields = jest.fn();
const deleteIndexPatternClick = jest.fn();
const { Provider } = createOpenSearchDashboardsReactContext({
...mockCoreStart,
...{
application: {
...mockCoreStart.application,
capabilities: {
...mockCoreStart.application.capabilities,
workspaces: { enabled: true },
},
},
uiSettings: { ...mockCoreStart.uiSettings, get: jest.fn().mockReturnValue(true) },
workspaces: {
...workspacesServiceMock.createStartContract(),
currentWorkspace$: new BehaviorSubject<WorkspaceObject | null>(props?.workspace),
},
navigationUI: {
HeaderControl: mockHeaderControl,
},
},
});

return (
<IntlProvider locale="en">
<Provider>
<IndexHeader
setDefault={setDefault}
refreshFields={refreshFields}
deleteIndexPatternClick={deleteIndexPatternClick}
indexPattern={indexPattern}
defaultIndex={props?.defaultIndex}
/>
</Provider>
</IntlProvider>
);
};

beforeEach(() => {
jest.clearAllMocks();
});

it('renders the set default button when index is not default and user is in workspace', () => {
yubonluo marked this conversation as resolved.
Show resolved Hide resolved
const mockHeaderControl = ({ controls }) => {
return controls?.[1]?.label ?? null;
};
const { getByText } = render(
getIndexHeader({
header: mockHeaderControl,
defaultIndex: 'no-default-index',
workspace: workspaceObject,
})
);

expect(getByText('Set as default index')).toBeInTheDocument();
});

it('does not renders the set default button when index is default and user is in workspace', () => {
const mockHeaderControl = ({ controls }) => {
return controls?.[1]?.label ?? null;
};
const { queryByText } = render(
getIndexHeader({
header: mockHeaderControl,
defaultIndex: 'default-index',
workspace: workspaceObject,
})
);

expect(queryByText('Set as default index')).toBeNull();
});

it('does not renders the set default button when index is not default and user is not in workspace', () => {
const mockHeaderControl = ({ controls }) => {
return controls?.[1]?.label ?? null;
};
const { queryByText } = render(
getIndexHeader({ header: mockHeaderControl, defaultIndex: 'no-default-index' })
);

expect(queryByText('Set as default index')).toBeNull();
});

it('does not renders the set default button when index is default and user is not in workspace', () => {
const mockHeaderControl = ({ controls }) => {
return controls?.[1]?.label ?? null;
};
const { queryByText } = render(
getIndexHeader({ header: mockHeaderControl, defaultIndex: 'default-index' })
);

expect(queryByText('Set as default index')).toBeNull();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ import {
} from '../services';
import { SavedObjectsTable } from './objects_table';
import { NavigationPublicPluginStart } from '../../../navigation/public';
import { formatUrlWithWorkspaceId } from '../../../../core/public/utils';
import { formatInspectUrl } from '../utils';

const SavedObjectsTablePage = ({
coreStart,
Expand Down Expand Up @@ -76,8 +76,6 @@ const SavedObjectsTablePage = ({
const itemsPerPage = coreStart.uiSettings.get<number>('savedObjects:perPage', 50);
const dateFormat = coreStart.uiSettings.get<string>('dateFormat');
const currentWorkspace = useObservable(coreStart.workspaces.currentWorkspace$);
const basePath = coreStart.http.basePath;
const visibleWsIds = useObservable(coreStart.workspaces.workspaceList$)?.map((ws) => ws.id) || [];

useEffect(() => {
setBreadcrumbs([
Expand Down Expand Up @@ -119,26 +117,8 @@ const SavedObjectsTablePage = ({
workspaces={coreStart.workspaces}
perPageConfig={itemsPerPage}
goInspectObject={(savedObject) => {
const { editUrl } = savedObject.meta;
let finalEditUrl = editUrl;
if (useUpdatedUX && finalEditUrl) {
finalEditUrl = finalEditUrl.replace(/^\/management\/opensearch-dashboards/, '/app');
}
if (finalEditUrl) {
let inAppUrl = basePath.prepend(finalEditUrl);
if (savedObject.workspaces?.length) {
if (currentWorkspace) {
inAppUrl = formatUrlWithWorkspaceId(finalEditUrl, currentWorkspace.id, basePath);
} else {
// find first workspace user have permission
const workspaceId = savedObject.workspaces.find((wsId) =>
visibleWsIds.includes(wsId)
);
if (workspaceId) {
inAppUrl = formatUrlWithWorkspaceId(finalEditUrl, workspaceId, basePath);
}
}
}
const inAppUrl = formatInspectUrl(savedObject, coreStart);
if (inAppUrl) {
return coreStart.application.navigateToUrl(inAppUrl);
}
}}
Expand Down
84 changes: 83 additions & 1 deletion src/plugins/saved_objects_management/public/utils.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,9 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { formatWorkspaceIdParams } from './utils';
import { coreMock } from '../../../core/public/mocks';
import { formatInspectUrl, formatWorkspaceIdParams } from './utils';
import { CoreStart } from 'opensearch-dashboards/public';

describe('Utils', () => {
it('formatWorkspaceIdParams with workspace null/undefined', async () => {
Expand Down Expand Up @@ -41,4 +43,84 @@ describe('Utils', () => {
});
expect(obj).toEqual({ foo: 'bar', availableWorkspaces: ['foo', 'bar'], workspaces: ['foo'] });
});

describe('formatInspectUrl', () => {
let mockCoreStart: CoreStart;
const savedObject = {
type: 'dashboard',
id: 'dashboard',
attributes: {},
references: [],
meta: {
editUrl: '/management/opensearch-dashboards/objects/savedDashboards/ID1',
},
};
const savedObjectWithWorkspaces = {
...savedObject,
workspaces: ['workspace1'],
};

beforeEach(() => {
jest.clearAllMocks();
mockCoreStart = coreMock.createStart();
mockCoreStart.application.capabilities = {
...mockCoreStart.application.capabilities,
workspaces: {
...mockCoreStart.application.capabilities.workspaces,
enabled: true,
},
};
mockCoreStart.uiSettings = {
...mockCoreStart.uiSettings,
get: jest.fn().mockReturnValue(true),
};
});

it('formats URL correctly when useUpdatedUX is false and workspace is disabled', () => {
mockCoreStart.application.capabilities = {
...mockCoreStart.application.capabilities,
workspaces: {
...mockCoreStart.application.capabilities.workspaces,
enabled: false,
},
};
mockCoreStart.uiSettings = {
...mockCoreStart.uiSettings,
get: jest.fn().mockReturnValue(false),
};
const result = formatInspectUrl(savedObject, mockCoreStart);
expect(result).toBe('/management/opensearch-dashboards/objects/savedDashboards/ID1');
});

it('formats URL correctly when useUpdatedUX is false, saved object does not belong to certain workspaces and not in current workspace', () => {
mockCoreStart.uiSettings = {
...mockCoreStart.uiSettings,
get: jest.fn().mockReturnValue(false),
};
const result = formatInspectUrl(savedObject, mockCoreStart);
expect(result).toBe('/management/opensearch-dashboards/objects/savedDashboards/ID1');
});

it('formats URL correctly when useUpdatedUX is true and in current workspace', () => {
const currentWorkspace = { id: 'workspace1', name: 'workspace1' };
mockCoreStart.workspaces.currentWorkspace$.next(currentWorkspace);
const result = formatInspectUrl(savedObjectWithWorkspaces, mockCoreStart);

expect(result).toBe('http://localhost/w/workspace1/app/objects/savedDashboards/ID1');
});

it('formats URL correctly when useUpdatedUX is true and saved object belongs to certain workspaces', () => {
mockCoreStart.workspaces.workspaceList$.next([{ id: 'workspace1', name: 'workspace1' }]);
const result = formatInspectUrl(savedObjectWithWorkspaces, mockCoreStart);

expect(result).toBe('http://localhost/w/workspace1/app/objects/savedDashboards/ID1');
});

it('formats URL correctly when useUpdatedUX is true and no workspace permission', () => {
yubonluo marked this conversation as resolved.
Show resolved Hide resolved
mockCoreStart.workspaces.workspaceList$.next([{ id: 'workspace2', name: 'workspace2' }]);
const result = formatInspectUrl(savedObjectWithWorkspaces, mockCoreStart);

expect(result).toBe('/app/objects/savedDashboards/ID1');
});
});
});
36 changes: 36 additions & 0 deletions src/plugins/saved_objects_management/public/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
* SPDX-License-Identifier: Apache-2.0
*/

import { CoreStart } from 'opensearch-dashboards/public';
import { formatUrlWithWorkspaceId } from '../../../core/public/utils';
import { SavedObjectWithMetadata } from './types';

export function formatWorkspaceIdParams<
T extends { workspaces?: string[] | null; availableWorkspaces?: string[] | null }
>(obj: T): T | Omit<T, 'workspaces' | 'availableWorkspaces'> {
Expand All @@ -12,3 +16,35 @@ export function formatWorkspaceIdParams<
}
return others;
}

export function formatInspectUrl(
savedObject: SavedObjectWithMetadata,
coreStart: CoreStart
): string | undefined {
const { editUrl } = savedObject.meta;
const useUpdatedUX = !!coreStart.uiSettings.get('home:useNewHomePage');
let finalEditUrl = editUrl;
if (useUpdatedUX && finalEditUrl) {
finalEditUrl = finalEditUrl.replace(/^\/management\/opensearch-dashboards/, '/app');
}
if (finalEditUrl) {
const basePath = coreStart.http.basePath;
let inAppUrl = basePath.prepend(finalEditUrl);
const workspaceEnabled = coreStart.application.capabilities.workspaces.enabled;
if (workspaceEnabled) {
const currentWorkspace = coreStart.workspaces.currentWorkspace$.value;
if (currentWorkspace) {
inAppUrl = formatUrlWithWorkspaceId(finalEditUrl, currentWorkspace.id, basePath);
} else {
const visibleWsIds = coreStart.workspaces.workspaceList$.value?.map((ws) => ws.id) || [];

// find first workspace user have permission
const workspaceId = savedObject?.workspaces?.find((wsId) => visibleWsIds.includes(wsId));
if (workspaceId) {
inAppUrl = formatUrlWithWorkspaceId(finalEditUrl, workspaceId, basePath);
}
}
}
return inAppUrl;
}
}
Loading