[Spaces] Add solution view select in UI (#186178)

This commit is contained in:
Sébastien Loix 2024-06-19 16:18:13 +01:00 committed by GitHub
parent 6e905c24dd
commit 37ca5c6bd9
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
20 changed files with 506 additions and 19 deletions

View file

@ -326,6 +326,7 @@ enabled:
- x-pack/test/functional/apps/search_playground/config.ts
- x-pack/test/functional/apps/snapshot_restore/config.ts
- x-pack/test/functional/apps/spaces/config.ts
- x-pack/test/functional/apps/spaces/solution_view_flag_enabled/config.ts
- x-pack/test/functional/apps/status_page/config.ts
- x-pack/test/functional/apps/transform/creation/index_pattern/config.ts
- x-pack/test/functional/apps/transform/creation/runtime_mappings_saved_search/config.ts

View file

@ -19,7 +19,8 @@
"home",
"management",
"usageCollection",
"cloud"
"cloud",
"cloudExperiments"
],
"requiredBundles": [
"esUiShared",

View file

@ -22,3 +22,5 @@ export const getSpacesFeatureDescription = () => {
export const DEFAULT_OBJECT_NOUN = i18n.translate('xpack.spaces.shareToSpace.objectNoun', {
defaultMessage: 'object',
});
export const SOLUTION_NAV_FEATURE_FLAG_NAME = 'solutionNavEnabled';

View file

@ -2,6 +2,7 @@
exports[`renders correctly 1`] = `
<SectionPanel
dataTestSubj="generalPanel"
title="General"
>
<EuiDescribedFormGroup

View file

@ -58,7 +58,7 @@ export class CustomizeSpace extends Component<Props, State> {
});
return (
<SectionPanel title={panelTitle}>
<SectionPanel title={panelTitle} dataTestSubj="generalPanel">
<EuiDescribedFormGroup
title={
<EuiTitle size="xs">

View file

@ -10,12 +10,13 @@ import { EuiButton } from '@elastic/eui';
import { waitFor } from '@testing-library/react';
import type { ReactWrapper } from 'enzyme';
import React from 'react';
import { of } from 'rxjs';
import { DEFAULT_APP_CATEGORIES } from '@kbn/core/public';
import { notificationServiceMock, scopedHistoryMock } from '@kbn/core/public/mocks';
import { KibanaFeature } from '@kbn/features-plugin/public';
import { featuresPluginMock } from '@kbn/features-plugin/public/mocks';
import { mountWithIntl } from '@kbn/test-jest-helpers';
import { findTestSubject, mountWithIntl } from '@kbn/test-jest-helpers';
import { ConfirmAlterActiveSpaceModal } from './confirm_alter_active_space_modal';
import { EnabledFeatures } from './enabled_features';
@ -105,6 +106,93 @@ describe('ManageSpacePage', () => {
});
});
it('shows solution view select when enabled', async () => {
const spacesManager = spacesManagerMock.create();
spacesManager.createSpace = jest.fn(spacesManager.createSpace);
spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space);
const wrapper = mountWithIntl(
<ManageSpacePage
spacesManager={spacesManager as unknown as SpacesManager}
getFeatures={featuresStart.getFeatures}
notifications={notificationServiceMock.createStartContract()}
history={history}
capabilities={{
navLinks: {},
management: {},
catalogue: {},
spaces: { manage: true },
}}
allowFeatureVisibility
isSolutionNavEnabled$={of(true)}
/>
);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('input[name="name"]')).toHaveLength(1);
});
expect(findTestSubject(wrapper, 'navigationPanel')).toHaveLength(1);
});
it('hides solution view select when not enabled or undefined', async () => {
const spacesManager = spacesManagerMock.create();
spacesManager.createSpace = jest.fn(spacesManager.createSpace);
spacesManager.getActiveSpace = jest.fn().mockResolvedValue(space);
{
const wrapper = mountWithIntl(
<ManageSpacePage
spacesManager={spacesManager as unknown as SpacesManager}
getFeatures={featuresStart.getFeatures}
notifications={notificationServiceMock.createStartContract()}
history={history}
capabilities={{
navLinks: {},
management: {},
catalogue: {},
spaces: { manage: true },
}}
allowFeatureVisibility
/>
);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('input[name="name"]')).toHaveLength(1);
});
expect(findTestSubject(wrapper, 'navigationPanel')).toHaveLength(0);
}
{
const wrapper = mountWithIntl(
<ManageSpacePage
spacesManager={spacesManager as unknown as SpacesManager}
getFeatures={featuresStart.getFeatures}
notifications={notificationServiceMock.createStartContract()}
history={history}
capabilities={{
navLinks: {},
management: {},
catalogue: {},
spaces: { manage: true },
}}
allowFeatureVisibility
isSolutionNavEnabled$={of(false)}
/>
);
await waitFor(() => {
wrapper.update();
expect(wrapper.find('input[name="name"]')).toHaveLength(1);
});
expect(findTestSubject(wrapper, 'navigationPanel')).toHaveLength(0);
}
});
it('shows feature visibility controls when allowed', async () => {
const spacesManager = spacesManagerMock.create();
spacesManager.createSpace = jest.fn(spacesManager.createSpace);

View file

@ -18,6 +18,7 @@ import {
} from '@elastic/eui';
import { difference } from 'lodash';
import React, { Component } from 'react';
import type { Observable, Subscription } from 'rxjs';
import type { Capabilities, NotificationsStart, ScopedHistory } from '@kbn/core/public';
import { SectionLoading } from '@kbn/es-ui-shared-plugin/public';
@ -29,6 +30,7 @@ import { ConfirmAlterActiveSpaceModal } from './confirm_alter_active_space_modal
import { CustomizeSpace } from './customize_space';
import { DeleteSpacesButton } from './delete_spaces_button';
import { EnabledFeatures } from './enabled_features';
import { SolutionView } from './solution_view';
import type { Space } from '../../../common';
import { isReservedSpace } from '../../../common';
import { getSpacesFeatureDescription } from '../../constants';
@ -54,6 +56,7 @@ interface Props {
capabilities: Capabilities;
history: ScopedHistory;
allowFeatureVisibility: boolean;
isSolutionNavEnabled$?: Observable<boolean>;
}
interface State {
@ -67,10 +70,13 @@ interface State {
isInvalid: boolean;
error?: string;
};
isSolutionNavEnabled: boolean;
}
export class ManageSpacePage extends Component<Props, State> {
private readonly validator: SpaceValidator;
private initialSpaceState: State['space'] | null = null;
private subscription: Subscription | null = null;
constructor(props: Props) {
super(props);
@ -83,6 +89,7 @@ export class ManageSpacePage extends Component<Props, State> {
color: getSpaceColor({}),
},
features: [],
isSolutionNavEnabled: false,
};
}
@ -107,6 +114,12 @@ export class ManageSpacePage extends Component<Props, State> {
}),
});
}
if (this.props.isSolutionNavEnabled$) {
this.subscription = this.props.isSolutionNavEnabled$.subscribe((isEnabled) => {
this.setState({ isSolutionNavEnabled: isEnabled });
});
}
}
public async componentDidUpdate(previousProps: Props) {
@ -115,6 +128,12 @@ export class ManageSpacePage extends Component<Props, State> {
}
}
public componentWillUnmount() {
if (this.subscription) {
this.subscription.unsubscribe();
}
}
public render() {
if (!this.props.capabilities.spaces.manage) {
return (
@ -161,6 +180,13 @@ export class ManageSpacePage extends Component<Props, State> {
validator={this.validator}
/>
{this.state.isSolutionNavEnabled && (
<>
<EuiSpacer size="l" />
<SolutionView space={this.state.space} onChange={this.onSpaceChange} />
</>
)}
{this.props.allowFeatureVisibility && (
<>
<EuiSpacer />
@ -298,8 +324,10 @@ export class ManageSpacePage extends Component<Props, State> {
const haveDisabledFeaturesChanged =
space.disabledFeatures.length !== originalSpace.disabledFeatures.length ||
difference(space.disabledFeatures, originalSpace.disabledFeatures).length > 0;
const hasSolutionViewChanged =
this.state.space.solution !== this.initialSpaceState?.solution;
if (editingActiveSpace && haveDisabledFeaturesChanged) {
if (editingActiveSpace && (haveDisabledFeaturesChanged || hasSolutionViewChanged)) {
this.setState({
showAlteringActiveSpaceDialog: true,
});
@ -326,17 +354,19 @@ export class ManageSpacePage extends Component<Props, State> {
onLoadSpace(space);
}
this.initialSpaceState = {
...space,
avatarType: space.imageUrl ? 'image' : 'initials',
initials: space.initials || getSpaceInitials(space),
color: space.color || getSpaceColor(space),
customIdentifier: false,
customAvatarInitials:
!!space.initials && getSpaceInitials({ name: space.name }) !== space.initials,
customAvatarColor: !!space.color && getSpaceColor({ name: space.name }) !== space.color,
};
this.setState({
space: {
...space,
avatarType: space.imageUrl ? 'image' : 'initials',
initials: space.initials || getSpaceInitials(space),
color: space.color || getSpaceColor(space),
customIdentifier: false,
customAvatarInitials:
!!space.initials && getSpaceInitials({ name: space.name }) !== space.initials,
customAvatarColor: !!space.color && getSpaceColor({ name: space.name }) !== space.color,
},
space: { ...this.initialSpaceState },
features,
originalSpace: space,
isLoading: false,
@ -369,6 +399,7 @@ export class ManageSpacePage extends Component<Props, State> {
disabledFeatures = [],
imageUrl,
avatarType,
solution,
} = this.state.space;
const params = {
@ -379,6 +410,7 @@ export class ManageSpacePage extends Component<Props, State> {
color: color ? hsvToHex(hexToHsv(color)).toUpperCase() : color, // Convert 3 digit hex codes to 6 digits since Spaces API requires 6 digits
disabledFeatures,
imageUrl: avatarType === 'image' ? imageUrl : '',
solution,
};
let action;

View file

@ -13,12 +13,13 @@ import React, { Component, Fragment } from 'react';
interface Props {
iconType?: IconType;
title: string | ReactNode;
dataTestSubj?: string;
}
export class SectionPanel extends Component<Props, {}> {
public render() {
return (
<EuiPanel hasShadow={false} hasBorder={true}>
<EuiPanel hasShadow={false} hasBorder={true} data-test-subj={this.props.dataTestSubj}>
{this.getTitle()}
{this.getForm()}
</EuiPanel>

View file

@ -0,0 +1,8 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
export { SolutionView } from './solution_view';

View file

@ -0,0 +1,153 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import type { EuiSuperSelectOption, EuiThemeComputed } from '@elastic/eui';
import {
EuiFlexGroup,
EuiFlexItem,
EuiFormRow,
EuiIcon,
EuiSpacer,
EuiSuperSelect,
EuiText,
EuiTitle,
useEuiTheme,
} from '@elastic/eui';
import type { FunctionComponent } from 'react';
import React from 'react';
import { i18n } from '@kbn/i18n';
import { FormattedMessage } from '@kbn/i18n-react';
import type { Space } from '../../../../common';
import { SectionPanel } from '../section_panel';
type SolutionView = Space['solution'];
const getOptions = ({ size }: EuiThemeComputed): Array<EuiSuperSelectOption<SolutionView>> => {
const iconCss = { marginRight: size.m };
return [
{
value: 'es',
inputDisplay: (
<>
<EuiIcon type="logoElasticsearch" css={iconCss} />
{i18n.translate(
'xpack.spaces.management.manageSpacePage.solutionViewSelect.searchOptionLabel',
{
defaultMessage: 'Search',
}
)}
</>
),
'data-test-subj': 'solutionViewEsOption',
},
{
value: 'oblt',
inputDisplay: (
<>
<EuiIcon type="logoObservability" css={iconCss} />
{i18n.translate(
'xpack.spaces.management.manageSpacePage.solutionViewSelect.obltOptionLabel',
{
defaultMessage: 'Observability',
}
)}
</>
),
'data-test-subj': 'solutionViewObltOption',
},
{
value: 'security',
inputDisplay: (
<>
<EuiIcon type="logoSecurity" css={iconCss} />
{i18n.translate(
'xpack.spaces.management.manageSpacePage.solutionViewSelect.securityOptionLabel',
{
defaultMessage: 'Security',
}
)}
</>
),
'data-test-subj': 'solutionViewSecurityOption',
},
{
value: 'classic',
inputDisplay: (
<>
<EuiIcon type="logoKibana" css={iconCss} />
{i18n.translate(
'xpack.spaces.management.manageSpacePage.solutionViewSelect.classicOptionLabel',
{
defaultMessage: 'Classic',
}
)}
</>
),
'data-test-subj': 'solutionViewClassicOption',
},
];
};
interface Props {
space: Partial<Space>;
onChange: (space: Partial<Space>) => void;
}
export const SolutionView: FunctionComponent<Props> = ({ space, onChange }) => {
const { euiTheme } = useEuiTheme();
return (
<SectionPanel
title={i18n.translate('xpack.spaces.management.manageSpacePage.navigationTitle', {
defaultMessage: 'Navigation',
})}
dataTestSubj="navigationPanel"
>
<EuiFlexGroup>
<EuiFlexItem>
<EuiTitle size="xs">
<h3>
<FormattedMessage
id="xpack.spaces.management.manageSpacePage.setSolutionViewMessage"
defaultMessage="Set solution view"
/>
</h3>
</EuiTitle>
<EuiSpacer size="s" />
<EuiText size="s" color="subdued">
<p>
<FormattedMessage
id="xpack.spaces.management.manageSpacePage.setSolutionViewDescription"
defaultMessage="Determines the navigation all users will see for this space. Each solution view contains features from Analytics tools and Management."
/>
</p>
</EuiText>
</EuiFlexItem>
<EuiFlexItem>
<EuiFormRow
label={i18n.translate('xpack.spaces.management.navigation.solutionViewLabel', {
defaultMessage: 'Solution view',
})}
fullWidth
>
<EuiSuperSelect
options={getOptions(euiTheme)}
valueOfSelected={space.solution}
data-test-subj="solutionViewSelect"
onChange={(solution) => {
onChange({ ...space, solution });
}}
/>
</EuiFormRow>
</EuiFlexItem>
</EuiFlexGroup>
</SectionPanel>
);
};

View file

@ -125,7 +125,7 @@ describe('spacesManagementApp', () => {
css="You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop)."
data-test-subj="kbnRedirectAppLink"
>
Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/create","search":"","hash":""}},"allowFeatureVisibility":true}
Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"history":{"action":"PUSH","length":1,"location":{"pathname":"/create","search":"","hash":""}},"allowFeatureVisibility":true,"isSolutionNavEnabled$":{}}
</div>
</div>
`);
@ -158,7 +158,7 @@ describe('spacesManagementApp', () => {
css="You have tried to stringify object returned from \`css\` function. It isn't supposed to be used directly (e.g. as value of the \`className\` prop), but rather handed to emotion so it can handle it (e.g. as value of \`css\` prop)."
data-test-subj="kbnRedirectAppLink"
>
Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}},"allowFeatureVisibility":true}
Spaces Edit Page: {"capabilities":{"catalogue":{},"management":{},"navLinks":{}},"notifications":{"toasts":{}},"spacesManager":{"onActiveSpaceChange$":{}},"spaceId":"some-space","history":{"action":"PUSH","length":1,"location":{"pathname":"/edit/some-space","search":"","hash":""}},"allowFeatureVisibility":true,"isSolutionNavEnabled$":{}}
</div>
</div>
`);

View file

@ -8,6 +8,7 @@
import React from 'react';
import { render, unmountComponentAtNode } from 'react-dom';
import { useParams } from 'react-router-dom';
import { from, of, shareReplay } from 'rxjs';
import type { StartServicesAccessor } from '@kbn/core/public';
import { i18n } from '@kbn/i18n';
@ -20,6 +21,7 @@ import { Route, Router, Routes } from '@kbn/shared-ux-router';
import type { Space } from '../../common';
import type { ConfigType } from '../config';
import { SOLUTION_NAV_FEATURE_FLAG_NAME } from '../constants';
import type { PluginsStart } from '../plugin';
import type { SpacesManager } from '../spaces_manager';
@ -43,8 +45,15 @@ export const spacesManagementApp = Object.freeze({
title,
async mount({ element, setBreadcrumbs, history }) {
const [[coreStart, { features }], { SpacesGridPage }, { ManageSpacePage }] =
await Promise.all([getStartServices(), import('./spaces_grid'), import('./edit_space')]);
const [
[coreStart, { features, cloud, cloudExperiments }],
{ SpacesGridPage },
{ ManageSpacePage },
] = await Promise.all([
getStartServices(),
import('./spaces_grid'),
import('./edit_space'),
]);
const spacesFirstBreadcrumb = {
text: title,
@ -54,6 +63,17 @@ export const spacesManagementApp = Object.freeze({
chrome.docTitle.change(title);
const onCloud = Boolean(cloud?.isCloudEnabled);
const isSolutionNavEnabled$ =
// Only available on Cloud and if the Launch Darkly flag is turned on
onCloud && cloudExperiments
? from(
cloudExperiments
.getVariation(SOLUTION_NAV_FEATURE_FLAG_NAME, false)
.catch(() => false)
).pipe(shareReplay(1))
: of(false);
const SpacesGridPageWithBreadcrumbs = () => {
setBreadcrumbs([{ ...spacesFirstBreadcrumb, href: undefined }]);
return (
@ -87,6 +107,7 @@ export const spacesManagementApp = Object.freeze({
spacesManager={spacesManager}
history={history}
allowFeatureVisibility={config.allowFeatureVisibility}
isSolutionNavEnabled$={isSolutionNavEnabled$}
/>
);
};
@ -113,6 +134,7 @@ export const spacesManagementApp = Object.freeze({
onLoadSpace={onLoadSpace}
history={history}
allowFeatureVisibility={config.allowFeatureVisibility}
isSolutionNavEnabled$={isSolutionNavEnabled$}
/>
);
};

View file

@ -5,6 +5,8 @@
* 2.0.
*/
import type { CloudExperimentsPluginStart } from '@kbn/cloud-experiments-plugin/common';
import type { CloudSetup, CloudStart } from '@kbn/cloud-plugin/public';
import type { CoreSetup, CoreStart, Plugin, PluginInitializerContext } from '@kbn/core/public';
import type { FeaturesPluginStart } from '@kbn/features-plugin/public';
import type { HomePublicPluginSetup } from '@kbn/home-plugin/public';
@ -23,11 +25,14 @@ import { getUiApi } from './ui_api';
export interface PluginsSetup {
home?: HomePublicPluginSetup;
management?: ManagementSetup;
cloud?: CloudSetup;
}
export interface PluginsStart {
features: FeaturesPluginStart;
management?: ManagementStart;
cloud?: CloudStart;
cloudExperiments?: CloudExperimentsPluginStart;
}
/**

View file

@ -37,6 +37,7 @@
"@kbn/utility-types-jest",
"@kbn/security-plugin-types-public",
"@kbn/cloud-plugin",
"@kbn/cloud-experiments-plugin",
],
"exclude": [
"target/**/*",

View file

@ -0,0 +1,36 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrProviderContext } from '../../ftr_provider_context';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'settings', 'security', 'spaceSelector']);
const testSubjects = getService('testSubjects');
describe('edit space', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
});
after(async () => {
await kibanaServer.savedObjects.cleanStandardList();
});
describe('solution view', () => {
it('does not show solution view panel', async () => {
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/default', {
shouldUseHashForSubUrl: false,
});
await testSubjects.existOrFail('spaces-edit-page');
await testSubjects.existOrFail('spaces-edit-page > generalPanel');
await testSubjects.missingOrFail('spaces-edit-page > navigationPanel');
});
});
});
}

View file

@ -13,5 +13,6 @@ export default function spacesApp({ loadTestFile }: FtrProviderContext) {
loadTestFile(require.resolve('./feature_controls/spaces_security'));
loadTestFile(require.resolve('./spaces_selection'));
loadTestFile(require.resolve('./enter_space'));
loadTestFile(require.resolve('./create_edit_space'));
});
}

View file

@ -0,0 +1,31 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrConfigProviderContext } from '@kbn/test';
export default async function ({ readConfigFile }: FtrConfigProviderContext) {
const functionalConfig = await readConfigFile(require.resolve('../../../config.base.js'));
return {
...functionalConfig.getAll(),
testFiles: [require.resolve('.')],
kbnTestServer: {
...functionalConfig.get('kbnTestServer'),
serverArgs: [
...functionalConfig.get('kbnTestServer.serverArgs'),
'--xpack.cloud_integrations.experiments.enabled=true',
'--xpack.cloud_integrations.experiments.launch_darkly.sdk_key=a_string',
'--xpack.cloud_integrations.experiments.launch_darkly.client_id=a_string',
'--xpack.cloud_integrations.experiments.flag_overrides.solutionNavEnabled=true',
// Note: the base64 string in the cloud.id config contains the ES endpoint required in the functional tests
'--xpack.cloud.id=ftr_fake_cloud_id:aGVsbG8uY29tOjQ0MyRFUzEyM2FiYyRrYm4xMjNhYmM=',
'--xpack.cloud.base_url=https://cloud.elastic.co',
'--xpack.cloud.deployment_url=/deployments/deploymentId',
],
},
};
}

View file

@ -0,0 +1,69 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function ({ getPageObjects, getService }: FtrProviderContext) {
const kibanaServer = getService('kibanaServer');
const PageObjects = getPageObjects(['common', 'settings', 'security', 'spaceSelector']);
const testSubjects = getService('testSubjects');
const find = getService('find');
describe('edit space', () => {
before(async () => {
await kibanaServer.savedObjects.cleanStandardList();
});
after(async () => {
await kibanaServer.savedObjects.cleanStandardList();
});
describe('solution view', () => {
it('does show the solution view panel', async () => {
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/default', {
shouldUseHashForSubUrl: false,
});
await testSubjects.existOrFail('spaces-edit-page');
await testSubjects.existOrFail('spaces-edit-page > generalPanel');
await testSubjects.existOrFail('spaces-edit-page > navigationPanel');
});
it('changes the space solution and updates the side navigation', async () => {
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/default', {
shouldUseHashForSubUrl: false,
});
// Make sure we are on the classic side nav
await testSubjects.existOrFail('mgtSideBarNav');
await testSubjects.missingOrFail('searchSideNav');
// change to Enterprise Search
await PageObjects.spaceSelector.changeSolutionView('es');
await PageObjects.spaceSelector.clickSaveSpaceCreation();
await PageObjects.spaceSelector.confirmModal();
await find.waitForDeletedByCssSelector('.kibanaWelcomeLogo');
// Search side nav is loaded
await testSubjects.existOrFail('searchSideNav');
await testSubjects.missingOrFail('mgtSideBarNav');
// change back to classic
await PageObjects.common.navigateToUrl('management', 'kibana/spaces/edit/default', {
shouldUseHashForSubUrl: false,
});
await PageObjects.spaceSelector.changeSolutionView('classic');
await PageObjects.spaceSelector.clickSaveSpaceCreation();
await PageObjects.spaceSelector.confirmModal();
await testSubjects.existOrFail('mgtSideBarNav');
await testSubjects.missingOrFail('searchSideNav');
});
});
});
}

View file

@ -0,0 +1,14 @@
/*
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
* or more contributor license agreements. Licensed under the Elastic License
* 2.0; you may not use this file except in compliance with the Elastic License
* 2.0.
*/
import { FtrProviderContext } from '../../../ftr_provider_context';
export default function spacesApp({ loadTestFile }: FtrProviderContext) {
describe('Spaces app (with solution view)', function spacesAppTestSuite() {
loadTestFile(require.resolve('./create_edit_space'));
});
}

View file

@ -120,6 +120,22 @@ export class SpaceSelectorPageObject extends FtrService {
await this.testSubjects.setValue('euiColorPickerAnchor', hexValue);
}
async openSolutionViewSelect() {
const solutionViewSelect = await this.testSubjects.find('solutionViewSelect');
const classes = await solutionViewSelect.getAttribute('class');
const isOpen = classes?.includes('isOpen') ?? false;
if (!isOpen) {
await solutionViewSelect.click();
}
}
async changeSolutionView(solution: 'es' | 'oblt' | 'security' | 'classic') {
await this.openSolutionViewSelect();
const serialized = solution.charAt(0).toUpperCase() + solution.slice(1);
await this.testSubjects.click(`solutionView${serialized}Option`);
}
async clickShowFeatures() {
await this.testSubjects.click('show-hide-section-link');
}
@ -208,6 +224,11 @@ export class SpaceSelectorPageObject extends FtrService {
await this.testSubjects.click('confirmModalConfirmButton');
}
// Generic for any confirm modal
async confirmModal() {
await this.testSubjects.click('confirmModalConfirmButton');
}
async clickOnSpaceb() {
await this.testSubjects.click('space-avatar-space_b');
}